From 350eb489c22e6bac20de92284193a87af63c52a9 Mon Sep 17 00:00:00 2001 From: Shpuld Shpuldson Date: Mon, 2 Nov 2020 15:46:49 +0200 Subject: [PATCH] add favicon badge for unread notifs --- CHANGELOG.md | 1 + index.html | 2 +- src/boot/after_store.js | 3 + src/components/notifications/notifications.js | 3 + .../favicon_service/favicon_service.js | 56 ++++++++++++++++++ static/dev_favicon.png | Bin 0 -> 7528 bytes 6 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 src/services/favicon_service/favicon_service.js create mode 100644 static/dev_favicon.png diff --git a/CHANGELOG.md b/CHANGELOG.md index 68cab40c..997889fe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - Import/export a muted users - Proper handling of deletes when using websocket streaming - Added optimistic chat message sending, so you can start writing next message before the previous one has been sent +- Added a small red badge to the favicon when there's unread notifications ### Fixed - Fixed chats list not updating its order when new messages come in diff --git a/index.html b/index.html index 1ff944d9..9ac0cfe9 100644 --- a/index.html +++ b/index.html @@ -5,7 +5,7 @@ Pleroma - + diff --git a/src/boot/after_store.js b/src/boot/after_store.js index 3cbbf020..b472fcf6 100644 --- a/src/boot/after_store.js +++ b/src/boot/after_store.js @@ -7,6 +7,7 @@ import { getOrCreateApp, getClientToken } from '../services/new_api/oauth.js' import backendInteractorService from '../services/backend_interactor_service/backend_interactor_service.js' import { CURRENT_VERSION } from '../services/theme_data/theme_data.service.js' import { applyTheme } from '../services/style_setter/style_setter.js' +import FaviconService from '../services/favicon_service/favicon_service.js' let staticInitialResults = null @@ -326,6 +327,8 @@ const afterStoreSetup = async ({ store, i18n }) => { const width = windowWidth() store.dispatch('setMobileLayout', width <= 800) + FaviconService.initFaviconService() + const overrides = window.___pleromafe_dev_overrides || {} const server = (typeof overrides.target !== 'undefined') ? overrides.target : window.location.origin store.dispatch('setInstanceOption', { name: 'server', value: server }) diff --git a/src/components/notifications/notifications.js b/src/components/notifications/notifications.js index 4b479e13..49258563 100644 --- a/src/components/notifications/notifications.js +++ b/src/components/notifications/notifications.js @@ -6,6 +6,7 @@ import { filteredNotificationsFromStore, unseenNotificationsFromStore } from '../../services/notification_utils/notification_utils.js' +import FaviconService from '../../services/favicon_service/favicon_service.js' import { library } from '@fortawesome/fontawesome-svg-core' import { faCircleNotch } from '@fortawesome/free-solid-svg-icons' @@ -75,8 +76,10 @@ const Notifications = { watch: { unseenCountTitle (count) { if (count > 0) { + FaviconService.drawFaviconBadge() this.$store.dispatch('setPageTitle', `(${count})`) } else { + FaviconService.clearFaviconBadge() this.$store.dispatch('setPageTitle', '') } } diff --git a/src/services/favicon_service/favicon_service.js b/src/services/favicon_service/favicon_service.js new file mode 100644 index 00000000..8e3f1170 --- /dev/null +++ b/src/services/favicon_service/favicon_service.js @@ -0,0 +1,56 @@ +import { find } from 'lodash' + +const createFaviconService = () => { + let favimg, favcanvas, favcontext, favicon + const faviconWidth = 48 + const faviconHeight = 48 + const strokeColor = 'rgb(200, 0, 0)' + const fillColor = 'rgb(255, 90, 90)' + const badgeRadius = 12 + + const initFaviconService = () => { + const nodes = document.getElementsByTagName('link') + favicon = find(nodes, node => node.rel === 'icon') + if (favicon) { + favcanvas = document.createElement('canvas') + favcanvas.width = faviconWidth + favcanvas.height = faviconHeight + favimg = new Image() + favimg.src = favicon.href + favcontext = favcanvas.getContext('2d') + } + } + + const clearFaviconBadge = () => { + if (!favimg || !favcontext || !favicon) return + + favcontext.clearRect(0, 0, faviconWidth, faviconHeight) + favcontext.drawImage(favimg, 0, 0, favimg.width, favimg.height, 0, 0, faviconWidth, faviconHeight) + favicon.href = favcanvas.toDataURL('image/png') + } + + const drawFaviconBadge = () => { + if (!favimg || !favcontext || !favcontext) return + + clearFaviconBadge() + + favcontext.drawImage(favimg, 0, 0, favimg.width, favimg.height, 0, 0, faviconWidth, faviconHeight) + favcontext.fillStyle = fillColor + favcontext.strokeStyle = strokeColor + favcontext.beginPath() + favcontext.arc(faviconWidth - badgeRadius, faviconHeight - badgeRadius, badgeRadius, 0, 2 * Math.PI, false) + favcontext.fill() + favcontext.stroke() + favicon.href = favcanvas.toDataURL('image/png') + } + + return { + initFaviconService, + clearFaviconBadge, + drawFaviconBadge + } +} + +const FaviconService = createFaviconService() + +export default FaviconService diff --git a/static/dev_favicon.png b/static/dev_favicon.png new file mode 100644 index 0000000000000000000000000000000000000000..8b53d74622460511095714ec2893eda11b2c0063 GIT binary patch literal 7528 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 literal 0 HcmV?d00001