From 9c9a9ee261fd933f24ea572d197c536e6b619d88 Mon Sep 17 00:00:00 2001 From: ThatOneCalculator <kainoa@t1c.dev> Date: Tue, 6 Jun 2023 18:43:05 -0700 Subject: [PATCH] =?UTF-8?q?feat:=20=E2=9C=A8=20patron=20labels?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- locales/en-US.yml | 5 ++ .../src/server/api/endpoints/patrons.ts | 4 ++ packages/client/src/instance.ts | 8 ++++ packages/client/src/pages/about-calckey.vue | 5 +- packages/client/src/pages/user/home.vue | 48 +++++++++++++++---- 5 files changed, 59 insertions(+), 11 deletions(-) diff --git a/locales/en-US.yml b/locales/en-US.yml index 2cfb9c47b..e65ed8bee 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -1099,6 +1099,11 @@ noGraze: "Please disable the \"Graze for Mastodon\" browser extension, as it int with Calckey." silencedWarning: "This page is showing because these users are from servers your admin silenced, so they may potentially be spam." +isBot: "This account is a bot" +isLocked: "This account has follow approvals" +isModerator: "Moderator" +isAdmin: "Administrator" +isPatron: "Calckey Patron" _sensitiveMediaDetection: description: "Reduces the effort of server moderation through automatically recognizing diff --git a/packages/backend/src/server/api/endpoints/patrons.ts b/packages/backend/src/server/api/endpoints/patrons.ts index d6ac6c397..aa9d25cf0 100644 --- a/packages/backend/src/server/api/endpoints/patrons.ts +++ b/packages/backend/src/server/api/endpoints/patrons.ts @@ -1,4 +1,5 @@ import define from "../define.js"; +import Logger from "@/services/logger.js"; export const meta = { tags: ["meta"], @@ -22,6 +23,9 @@ export default define(meta, paramDef, async () => { .then((response) => response.json()) .then((data) => { patrons = data["patrons"]; + }) + .catch((error) => { + console.error("Error fetching patrons:", error); }); return patrons; diff --git a/packages/client/src/instance.ts b/packages/client/src/instance.ts index 3381684a0..4b2c6a953 100644 --- a/packages/client/src/instance.ts +++ b/packages/client/src/instance.ts @@ -5,6 +5,7 @@ import type * as Misskey from "calckey-js"; // TODO: 他のタブと永続化されたstateを同期 const instanceData = localStorage.getItem("instance"); +const patronData = localStorage.getItem("patrons"); // TODO: instanceをリアクティブにするかは再考の余地あり @@ -16,6 +17,8 @@ export const instance: Misskey.entities.DetailedInstanceMetadata = reactive( }, ); +export const patrons = patronData || []; + export async function fetchInstance() { const meta = await api("meta", { detail: true, @@ -28,6 +31,11 @@ export async function fetchInstance() { localStorage.setItem("instance", JSON.stringify(instance)); } +export async function fetchPatrons() { + const patrons = await api("patrons"); + localStorage.setItem("patrons", JSON.stringify(patrons)); +} + export const emojiCategories = computed(() => { if (instance.emojis == null) return []; const categories = new Set(); diff --git a/packages/client/src/pages/about-calckey.vue b/packages/client/src/pages/about-calckey.vue index 1e16356a5..01350d840 100644 --- a/packages/client/src/pages/about-calckey.vue +++ b/packages/client/src/pages/about-calckey.vue @@ -163,14 +163,15 @@ import { i18n } from "@/i18n"; import { defaultStore } from "@/store"; import * as os from "@/os"; import { definePageMetadata } from "@/scripts/page-metadata"; - -const patrons = await os.api("patrons"); +import { patrons, fetchPatrons } from "@/instance"; let easterEggReady = false; let easterEggEmojis = $ref([]); let easterEggEngine = $ref(null); const containerEl = $ref<HTMLElement>(); +await fetchPatrons() + function iconLoaded() { const emojis = defaultStore.state.reactions; const containerWidth = containerEl?.offsetWidth; diff --git a/packages/client/src/pages/user/home.vue b/packages/client/src/pages/user/home.vue index 1926fee88..61f3d3168 100644 --- a/packages/client/src/pages/user/home.vue +++ b/packages/client/src/pages/user/home.vue @@ -54,7 +54,7 @@ /></span> <span v-if="user.isAdmin" - :title="i18n.ts.isAdmin" + v-tooltip.noDelay="i18n.ts.isAdmin" style="color: var(--badge)" ><i class="ph-bookmark-simple ph-fill ph-lg" @@ -62,22 +62,36 @@ ></span> <span v-if="!user.isAdmin && user.isModerator" - :title="i18n.ts.isModerator" + v-tooltip.noDelay="i18n.ts.isModerator" style="color: var(--badge)" ><i - class="ph-bookmark-simple ph-bold" + class="ph-bookmark-simple ph-bold ph-lg" ></i ></span> <span v-if="user.isLocked" - :title="i18n.ts.isLocked" + v-tooltip.noDelay="i18n.ts.isLocked" ><i class="ph-lock ph-bold ph-lg"></i ></span> <span v-if="user.isBot" - :title="i18n.ts.isBot" + v-tooltip.noDelay="i18n.ts.isBot" ><i class="ph-robot ph-bold ph-lg"></i ></span> + <span + v-if=" + patrons?.includes( + `@${user.username}@${ + user.host || host + }` + ) + " + v-tooltip.noDelay="i18n.ts.isPatron" + style="color: var(--badge)" + ><i + class="ph-hand-coins ph-bold ph-lg" + ></i + ></span> </div> </div> </div> @@ -110,7 +124,7 @@ /></span> <span v-if="user.isAdmin" - :title="i18n.ts.isAdmin" + v-tooltip.noDelay="i18n.ts.isAdmin" style="color: var(--badge)" ><i class="ph-bookmark-simple ph-fill ph-lg" @@ -118,18 +132,32 @@ ></span> <span v-if="!user.isAdmin && user.isModerator" - :title="i18n.ts.isModerator" + v-tooltip.noDelay="i18n.ts.isModerator" style="color: var(--badge)" ><i class="ph-bookmark-simple ph-bold"></i ></span> <span v-if="user.isLocked" - :title="i18n.ts.isLocked" + v-tooltip.noDelay="i18n.ts.isLocked" ><i class="ph-lock ph-bold ph-lg"></i ></span> - <span v-if="user.isBot" :title="i18n.ts.isBot" + <span + v-if="user.isBot" + v-tooltip.noDelay="i18n.ts.isBot" ><i class="ph-robot ph-bold ph-lg"></i ></span> + <span + v-if=" + patrons?.includes( + `@${user.username}@${ + user.host || host + }` + ) + " + v-tooltip.noDelay="i18n.ts.isPatron" + style="color: var(--badge)" + ><i class="ph-hand-coins ph-bold ph-lg"></i + ></span> </div> </div> <div class="follow-container"> @@ -324,6 +352,8 @@ import * as os from "@/os"; import { useRouter } from "@/router"; import { i18n } from "@/i18n"; import { $i } from "@/account"; +import { host } from "@/config"; +import { patrons } from "@/instance"; const XPhotos = defineAsyncComponent(() => import("./index.photos.vue")); const XActivity = defineAsyncComponent(() => import("./index.activity.vue"));