Connection status with Phoenix LiveView

RMAG news

Some notes on how to display the status of the connection by rendering images.

We define an <img> tag somewhere and want to set its src attribute depending upon the status.

<img width=“30px” alt=“line-status” id=“online-status” />

The connection status is accessible in the browser with the navigator.onLine boolean.

The connection change is captured by the window events online and offline.

We define the callbacks to these events: we append a given image file to the src attribute with some styling.

// /assets/js/onlineStatus.js

const domEl = document.getElementById(online-status);

const status = {
online: { src: /images/online.svg, bg: lavender, opacity: 0.8 },
offline: { src: /images/offline.svg, bg: tomato },
};

const setOnline = (el, { opacity = 1, bg, src }) => {
el.style.opacity = opacity;
el.src = src;
el.style.backgroundColor = bg;
};

const statusListener = () => {
window.onoffline = () => setOnline(domEl, status.offline);
window.ononline = () => setOnline(domEl, status.online);
};

export { statusListener };

We import and use this function in our Javascript “app.js” file:

// /assets/js/app.js

import {statusListener} from ./onlineStatus.js

statusListener()

It remains to solve the first render problem. Indeed, unless we set a src attribute on the <img> element above, which we don’t want, the Liveview DOM patching will rendering nothing. The snippet below doesn’t work:

navigator.onLine ? setOnline(domEl, status.online) : setOnline(domEl, status.offline)

This can be solved with the onBeforeElUpdated callback. It lets you perform your own DOM patching, independently from LiveView, on whatever DOM element you name from into a transformed DOM element named to.
This callback is attached to the dom property of the LiveSocket under the key onBeforeElUpdated. It looks like this:

We add a say firstRender function in our custom JS file and add our snippet:

// /assets/js/onlineStatus.js

const firstRender = (from, to) => {
if (from.getAttribute(id) === online-status) {
navigator.onLine
? setOnline(to, status.online)
: setOnline(to, status.offline);
}
}

export {firstRender, statusListener }

and then append it to our LiveSocket in the “app.js”:

// /assets/js/app.js
import {statusListener, firstRender} from ./onlineStatus.js

const liveview = new LiveSocket(/live, Socket, {
longPollFallbackMs: 2500,
params: { _csrf_token: csrfToken },
hooks: { onlineStatus },
dom: { onBeforeElUpdated: firstRender },
},
});

liveview.connect();

Examples of SVGs

The “/priv/static/images/online.svg” file example:

<svg xmlns=“http://www.w3.org/2000/svg” viewBox=“0 0 24 24”>
<path xmlns=“http://www.w3.org/2000/svg” d=“M 5.78125 4.1875 C 3.48125 6.0215 2 8.837 2 12 C 2 15.163 3.48125 17.9785 5.78125 19.8125 L 7.03125 18.25 C 5.19125 16.783 4 14.53 4 12 C 4 9.47 5.19125 7.217 7.03125 5.75 L 5.78125 4.1875 z M 18.25 4.1875 L 17 5.75 C 18.83 7.218 20 9.477 20 12 C 20 14.523 18.83 16.782 17 18.25 L 18.25 19.8125 C 20.538 17.9775 22 15.154 22 12 C 22 8.846 20.538 6.0215 18.25 4.1875 z M 8.28125 7.3125 C 6.90125 8.4125 6 10.102 6 12 C 6 13.898 6.90125 15.5875 8.28125 16.6875 L 9.53125 15.125 C 8.61225 14.391 8 13.265 8 12 C 8 10.735 8.61225 9.609 9.53125 8.875 L 8.28125 7.3125 z M 15.75 7.3125 L 14.5 8.90625 C 15.416 9.64025 16 10.739 16 12 C 16 13.262 15.415 14.36075 14.5 15.09375 L 15.75 16.6875 C 17.122 15.5875 18 13.892 18 12 C 18 10.108 17.123 8.4135 15.75 7.3125 z M 12 10.5 C 11.171573 10.5 10.5 11.171573 10.5 12 C 10.5 12.828427 11.171573 13.5 12 13.5 C 12.828427 13.5 13.5 12.828427 13.5 12 C 13.5 11.171573 12.828427 10.5 12 10.5 z”/>
</svg>

The “/priv/static/images/offline.svg” example:

<svg xmlns=“http://www.w3.org/2000/svg” viewBox=“0 0 24 24”>
<path xmlns=“http://www.w3.org/2000/svg” d=“M 3.90625 2.28125 L 2.5 3.71875 L 4.34375 5.5625 C 2.8847559 7.2990879 2 9.55875 2 12 C 2 15.162 3.48125 17.9785 5.78125 19.8125 L 7.03125 18.25 C 5.19125 16.783 4 14.53 4 12 C 4 10.111392 4.6813379 8.3690147 5.78125 7 L 7.1875 8.40625 C 6.44465 9.4038808 6 10.663008 6 12 C 6 13.898 6.90125 15.5875 8.28125 16.6875 L 9.53125 15.09375 C 8.61225 14.35975 8 13.265 8 12 C 8 11.214008 8.2597973 10.490055 8.65625 9.875 L 10.53125 11.75 C 10.517412 11.831789 10.5 11.91425 10.5 12 C 10.5 12.828 11.172 13.5 12 13.5 C 12.08575 13.5 12.168169 13.482588 12.25 13.46875 L 20.5 21.71875 L 21.90625 20.28125 L 3.90625 2.28125 z M 18.25 4.1875 L 17 5.75 C 18.83 7.217 20 9.477 20 12 C 20 13.194 19.722 14.2945 19.25 15.3125 L 20.71875 16.8125 C 21.51475 15.3805 22 13.751 22 12 C 22 8.846 20.538 6.0215 18.25 4.1875 z M 15.75 7.3125 L 14.5 8.875 C 15.416 9.609 16 10.738 16 12 C 16 12.027 16.001 12.0355 16 12.0625 L 17.71875 13.78125 C 17.89975 13.21225 18 12.628 18 12 C 18 10.108 17.123 8.4135 15.75 7.3125 z”/>
</svg>