From 88eb1a0c53c4079a085f30ec66a8d98539a005a0 Mon Sep 17 00:00:00 2001 From: KevinWh0 <45321184+ChaoticLeah@users.noreply.github.com> Date: Mon, 17 Jun 2024 17:44:00 +0200 Subject: [PATCH 1/5] fixes & add button to see if notification dot works --- locales/en-US.yml | 3 ++ locales/index.d.ts | 14 +++++- locales/ja-JP.yml | 5 +- packages/frontend/src/boot/main-boot.ts | 14 ++++++ .../frontend/src/pages/settings/general.vue | 13 ++++++ packages/frontend/src/scripts/favicon-dot.ts | 46 +++++++++++++++---- packages/frontend/src/ui/_common_/common.vue | 8 +--- 7 files changed, 84 insertions(+), 19 deletions(-) diff --git a/locales/en-US.yml b/locales/en-US.yml index 22d30a871c..c9f913822c 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -696,6 +696,9 @@ create: "Create" notificationSetting: "Notification settings" notificationSettingDesc: "Select the types of notification to display." enableFaviconNotificationDot: "Enable favicon notification dot" +verifyNotificationDotWorkingButton: "Check if the notification dot works on your instance" +notificationDotNotWorking: "Unfortunately, this instance does not support the notification dot feature at this time." +notificationDotWorking: "The notification dot is functioning properly on this instance." useGlobalSetting: "Use global settings" useGlobalSettingDesc: "If turned on, your account's notification settings will be used. If turned off, individual configurations can be made." other: "Other" diff --git a/locales/index.d.ts b/locales/index.d.ts index e5f77cf239..d53c98bf98 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -2793,9 +2793,21 @@ export interface Locale extends ILocale { */ "notificationSettingDesc": string; /** - * ファビコン通知ドットを有効にする + * 未読の通知があるときにタブのアイコンを目立たせる */ "enableFaviconNotificationDot": string; + /** + * 通知ドットがインスタンスで機能するかどうかを確認します。 + */ + "verifyNotificationDotWorkingButton": string; + /** + * 残念ながら、このインスタンスは現時点では通知ドット機能をサポートしていません。 + */ + "notificationDotNotWorking": string; + /** + * 通知ドットは、このインスタンスで正しく機能しています。 + */ + "notificationDotWorking": string; /** * グローバル設定を使う */ diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 454e0b3cee..fa4f5ba8fd 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -694,7 +694,10 @@ channel: "チャンネル" create: "作成" notificationSetting: "通知設定" notificationSettingDesc: "表示する通知の種別を選択してください。" -enableFaviconNotificationDot: "ファビコン通知ドットを有効にする" +enableFaviconNotificationDot: "未読の通知があるときにタブのアイコンを目立たせる" +verifyNotificationDotWorkingButton: "通知ドットがインスタンスで機能するかどうかを確認します。" +notificationDotNotWorking: "残念ながら、このインスタンスは現時点では通知ドット機能をサポートしていません。" +notificationDotWorking: "通知ドットは、このインスタンスで正しく機能しています。" useGlobalSetting: "グローバル設定を使う" useGlobalSettingDesc: "オンにすると、アカウントの通知設定が使用されます。オフにすると、個別に設定できるようになります。" other: "その他" diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts index 013fa7c37c..88942bec34 100644 --- a/packages/frontend/src/boot/main-boot.ts +++ b/packages/frontend/src/boot/main-boot.ts @@ -21,6 +21,7 @@ import { initializeSw } from '@/scripts/initialize-sw.js'; import { deckStore } from '@/ui/deck/deck-store.js'; import { emojiPicker } from '@/scripts/emoji-picker.js'; import { mainRouter } from '@/router/main.js'; +import { setFavIconDot } from '@/scripts/favicon-dot.js'; export async function mainBoot() { const { isClientUpdated } = await common(() => createApp( @@ -261,6 +262,15 @@ export async function mainBoot() { } } + function attemptShowNotificationDot() { + if (!$i) return; + if (defaultStore.state.enableFaviconNotificationDot) { + setFavIconDot(true); + } + } + + if ($i.hasUnreadNotification) attemptShowNotificationDot(); + const main = markRaw(stream.useChannel('main', null, 'System')); // 自分の情報が更新されたとき @@ -269,6 +279,8 @@ export async function mainBoot() { }); main.on('readAllNotifications', () => { + setFavIconDot(false); + updateAccount({ hasUnreadNotification: false, unreadNotificationsCount: 0, @@ -276,6 +288,8 @@ export async function mainBoot() { }); main.on('unreadNotification', () => { + attemptShowNotificationDot(); + const unreadNotificationsCount = ($i?.unreadNotificationsCount ?? 0) + 1; updateAccount({ hasUnreadNotification: true, diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue index d7bb6156e4..9dcb541dc9 100644 --- a/packages/frontend/src/pages/settings/general.vue +++ b/packages/frontend/src/pages/settings/general.vue @@ -141,6 +141,8 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts.enableFaviconNotificationDot }} + {{ i18n.ts.verifyNotificationDotWorkingButton }} + @@ -327,6 +329,7 @@ import { miLocalStorage } from '@/local-storage.js'; import { globalEvents } from '@/events.js'; import { claimAchievement } from '@/scripts/achievements.js'; import { deepMerge } from '@/scripts/merge.js'; +import { worksOnInstance } from '@/scripts/favicon-dot'; const lang = ref(miLocalStorage.getItem('lang')); const fontSize = ref(miLocalStorage.getItem('fontSize')); @@ -562,6 +565,16 @@ function testNotification(): void { }, 300); } +async function testNotificationDot() { + const success = await worksOnInstance(); + + if (success) { + os.toast(i18n.ts.notificationDotWorking); + } else { + os.toast(i18n.ts.notificationDotNotWorking); + } +} + function enableAllDataSaver() { const g = { ...defaultStore.state.dataSaver }; diff --git a/packages/frontend/src/scripts/favicon-dot.ts b/packages/frontend/src/scripts/favicon-dot.ts index ba6dcb5d30..a5972276e2 100644 --- a/packages/frontend/src/scripts/favicon-dot.ts +++ b/packages/frontend/src/scripts/favicon-dot.ts @@ -6,12 +6,12 @@ import tinycolor from 'tinycolor2'; class FavIconDot { - canvas: HTMLCanvasElement; - src: string | null = null; - ctx: CanvasRenderingContext2D | null = null; - faviconImage: HTMLImageElement | null = null; - faviconEL: HTMLLinkElement | undefined; - hasLoaded: Promise | undefined; + private readonly canvas: HTMLCanvasElement; + private src: string | null = null; + private ctx: CanvasRenderingContext2D | null = null; + private faviconImage: HTMLImageElement | null = null; + private faviconEL: HTMLLinkElement | undefined; + private hasLoaded: Promise | undefined; constructor() { this.canvas = document.createElement('canvas'); @@ -88,32 +88,58 @@ class FavIconDot { if (this.faviconEL) this.faviconEL.href = this.canvas.toDataURL('image/png'); } - async setVisible(isVisible: boolean) { + public async setVisible(isVisible: boolean) { // Wait for it to have loaded the icon await this.hasLoaded; this.drawIcon(); if (isVisible) this.drawDot(); this.setFavicon(); } + + public async worksOnInstance() { + try { + // Wait for it to have loaded the icon + await this.hasLoaded; + this.drawIcon(); + this.drawDot(); + this.canvas.toDataURL('image/png'); + } catch (error) { + return false; + } + return true; + } } let icon: FavIconDot | undefined = undefined; -export function setFavIconDot(visible: boolean) { +export async function setFavIconDot(visible: boolean) { const setIconVisibility = async () => { if (!icon) { icon = new FavIconDot(); await icon.setup(); } - (icon as FavIconDot).setVisible(visible); + try { + (icon as FavIconDot).setVisible(visible); + } catch (error) { + //Probably failed due to CORS and a dirty canvas + } }; // If document is already loaded, set visibility immediately if (document.readyState === 'complete') { - setIconVisibility(); + await setIconVisibility(); } else { // Otherwise, set visibility when window loads window.addEventListener('load', setIconVisibility); } } + +export async function worksOnInstance() { + if (!icon) { + icon = new FavIconDot(); + await icon.setup(); + } + + return await icon.worksOnInstance(); +} diff --git a/packages/frontend/src/ui/_common_/common.vue b/packages/frontend/src/ui/_common_/common.vue index b1fe8e54fc..65b27a0cc2 100644 --- a/packages/frontend/src/ui/_common_/common.vue +++ b/packages/frontend/src/ui/_common_/common.vue @@ -47,9 +47,8 @@ SPDX-License-Identifier: AGPL-3.0-only