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] 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 }}
+
{{ i18n.ts.position }}
@@ -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