Display Slack-like favicon unread badge (yoinked from Pleroma FE)
This commit is contained in:
parent
2e0316cf76
commit
69dcd6421a
3 changed files with 83 additions and 2 deletions
|
@ -5,6 +5,7 @@ import { withRouter } from 'react-router-dom';
|
|||
import { Helmet } from'react-helmet';
|
||||
import { getSettings } from 'soapbox/actions/settings';
|
||||
import sourceCode from 'soapbox/utils/code';
|
||||
import FaviconService from 'soapbox/utils/favicon_service';
|
||||
|
||||
const getNotifTotals = state => {
|
||||
const notifications = state.getIn(['notifications', 'unread'], 0);
|
||||
|
@ -35,8 +36,15 @@ class SoapboxHelmet extends React.Component {
|
|||
|
||||
addCounter = title => {
|
||||
const { unreadCount, demetricator } = this.props;
|
||||
if (unreadCount < 1 || demetricator) return title;
|
||||
return `(${unreadCount}) ${title}`;
|
||||
|
||||
if (unreadCount < 1 || demetricator) {
|
||||
// Erase badge when there are no notifications
|
||||
FaviconService.clearFaviconBadge();
|
||||
return title;
|
||||
} else {
|
||||
FaviconService.drawFaviconBadge();
|
||||
return `(${unreadCount}) ${title}`;
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
|
|
|
@ -12,6 +12,7 @@ import * as perf from './performance';
|
|||
import * as monitoring from './monitoring';
|
||||
import ready from './ready';
|
||||
import { NODE_ENV } from 'soapbox/build_config';
|
||||
import FaviconService from 'soapbox/utils/favicon_service';
|
||||
|
||||
function main() {
|
||||
perf.start('main()');
|
||||
|
@ -19,6 +20,9 @@ function main() {
|
|||
// Sentry
|
||||
monitoring.start();
|
||||
|
||||
// Favicon unread badge
|
||||
FaviconService.initFaviconService();
|
||||
|
||||
ready(() => {
|
||||
const mountNode = document.getElementById('soapbox');
|
||||
|
||||
|
|
69
app/soapbox/utils/favicon_service.js
Normal file
69
app/soapbox/utils/favicon_service.js
Normal file
|
@ -0,0 +1,69 @@
|
|||
// Adapted from Pleroma FE
|
||||
// https://git.pleroma.social/pleroma/pleroma-fe/-/blob/ef5bbc4e5f84bb9e8da76a0440eea5d656d36977/src/services/favicon_service/favicon_service.js
|
||||
|
||||
const createFaviconService = () => {
|
||||
const favicons = [];
|
||||
const faviconWidth = 128;
|
||||
const faviconHeight = 128;
|
||||
const badgeRadius = 32;
|
||||
|
||||
const initFaviconService = () => {
|
||||
const nodes = document.querySelectorAll('link[rel="icon"]');
|
||||
nodes.forEach(favicon => {
|
||||
if (favicon) {
|
||||
const favcanvas = document.createElement('canvas');
|
||||
favcanvas.width = faviconWidth;
|
||||
favcanvas.height = faviconHeight;
|
||||
const favimg = new Image();
|
||||
favimg.crossOrigin = 'anonymous';
|
||||
favimg.src = favicon.href;
|
||||
const favcontext = favcanvas.getContext('2d');
|
||||
favicons.push({ favcanvas, favimg, favcontext, favicon });
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const isImageLoaded = (img) => img.complete && img.naturalHeight !== 0;
|
||||
|
||||
const clearFaviconBadge = () => {
|
||||
if (favicons.length === 0) return;
|
||||
favicons.forEach(({ favimg, favcanvas, favcontext, favicon }) => {
|
||||
if (!favimg || !favcontext || !favicon) return;
|
||||
favcontext.clearRect(0, 0, faviconWidth, faviconHeight);
|
||||
if (isImageLoaded(favimg)) {
|
||||
favcontext.drawImage(favimg, 0, 0, favimg.width, favimg.height, 0, 0, faviconWidth, faviconHeight);
|
||||
}
|
||||
favicon.href = favcanvas.toDataURL('image/png');
|
||||
});
|
||||
};
|
||||
|
||||
const drawFaviconBadge = () => {
|
||||
if (favicons.length === 0) return;
|
||||
clearFaviconBadge();
|
||||
favicons.forEach(({ favimg, favcanvas, favcontext, favicon }) => {
|
||||
if (!favimg || !favcontext || !favcontext) return;
|
||||
|
||||
const style = getComputedStyle(document.body);
|
||||
const badgeColor = `${style.getPropertyValue('--badgeNotification') || 'rgb(240, 100, 100)'}`;
|
||||
|
||||
if (isImageLoaded(favimg)) {
|
||||
favcontext.drawImage(favimg, 0, 0, favimg.width, favimg.height, 0, 0, faviconWidth, faviconHeight);
|
||||
}
|
||||
favcontext.fillStyle = badgeColor;
|
||||
favcontext.beginPath();
|
||||
favcontext.arc(faviconWidth - badgeRadius, badgeRadius, badgeRadius, 0, 2 * Math.PI, false);
|
||||
favcontext.fill();
|
||||
favicon.href = favcanvas.toDataURL('image/png');
|
||||
});
|
||||
};
|
||||
|
||||
return {
|
||||
initFaviconService,
|
||||
clearFaviconBadge,
|
||||
drawFaviconBadge,
|
||||
};
|
||||
};
|
||||
|
||||
const FaviconService = createFaviconService();
|
||||
|
||||
export default FaviconService;
|
Loading…
Reference in a new issue