From aab1c769814b08c257cad3025422a0eea3bfba4f Mon Sep 17 00:00:00 2001 From: syuilo <4439005+syuilo@users.noreply.github.com> Date: Sun, 1 Sep 2024 08:44:55 +0900 Subject: [PATCH] wip --- .../src/components/EmCustomEmoji.vue | 5 +- .../src/components/EmInstanceTicker.vue | 17 ++--- .../src/components/EmNoteDetailed.vue | 2 +- packages/frontend-embed/src/pages/clip.vue | 2 +- packages/frontend-embed/src/pages/tag.vue | 2 +- .../src/pages/user-timeline.vue | 2 +- .../src/to-be-shared/media-proxy.ts | 53 ---------------- packages/frontend-shared/js/const.ts | 4 -- packages/frontend-shared/js/media-proxy.ts | 62 +++++++++++++++++++ packages/frontend/src/boot/common.ts | 33 +++++----- packages/frontend/src/boot/main-boot.ts | 5 +- .../frontend/src/components/MkChannelList.vue | 6 +- .../src/components/MkCropperDialog.vue | 7 ++- .../src/components/MkDonation.stories.impl.ts | 2 +- .../frontend/src/components/MkDonation.vue | 6 +- .../src/components/MkFeaturedPhotos.vue | 5 +- .../src/components/MkForgotPassword.vue | 7 ++- .../frontend/src/components/MkFormDialog.vue | 5 +- .../src/components/MkInstanceCardMini.vue | 7 ++- .../src/components/MkInstanceTicker.vue | 8 +-- packages/frontend/src/components/MkLink.vue | 7 ++- .../frontend/src/components/MkMediaImage.vue | 7 ++- .../frontend/src/components/MkMention.vue | 7 ++- packages/frontend/src/components/MkNote.vue | 9 +-- .../src/components/MkNoteDetailed.vue | 9 +-- packages/frontend/src/components/MkNotes.vue | 5 +- .../src/components/MkNotification.vue | 3 +- .../src/components/MkNotifications.vue | 5 +- .../frontend/src/components/MkPagination.vue | 5 +- .../frontend/src/components/MkPostForm.vue | 5 +- .../MkPushNotificationAllowButton.vue | 11 ++-- .../src/components/MkSignupDialog.form.vue | 29 ++++----- .../MkSignupDialog.rules.stories.impl.ts | 2 +- .../src/components/MkSignupDialog.rules.vue | 19 +++--- .../components/MkSourceCodeAvailablePopup.vue | 8 ++- .../frontend/src/components/MkTimeline.vue | 5 +- .../src/components/MkTutorialDialog.vue | 7 ++- .../frontend/src/components/MkUserInfo.vue | 6 +- .../frontend/src/components/MkUserList.vue | 6 +- .../frontend/src/components/MkUserPopup.vue | 10 +-- .../src/components/MkUserSetupDialog.vue | 12 ++-- .../src/components/MkVisitorDashboard.vue | 16 ++--- .../frontend/src/components/global/MkAd.vue | 9 +-- .../src/components/global/MkAvatar.vue | 9 +-- .../src/components/global/MkCustomEmoji.vue | 7 ++- .../src/components/global/MkError.vue | 6 +- .../frontend/src/components/global/MkUrl.vue | 7 ++- .../src/components/page/page.text.vue | 7 ++- packages/frontend/src/const.ts | 6 +- packages/frontend/src/local-storage.ts | 1 - packages/frontend/src/pages/_error_.vue | 7 ++- packages/frontend/src/pages/about-misskey.vue | 15 ++--- .../frontend/src/pages/about.overview.vue | 38 ++++++------ .../src/pages/admin/bot-protection.vue | 4 +- .../frontend/src/pages/admin/branding.vue | 4 +- .../src/pages/admin/email-settings.vue | 7 ++- .../src/pages/admin/external-services.vue | 4 +- packages/frontend/src/pages/admin/index.vue | 17 ++--- .../src/pages/admin/instance-block.vue | 4 +- .../frontend/src/pages/admin/moderation.vue | 4 +- .../src/pages/admin/object-storage.vue | 4 +- .../src/pages/admin/other-settings.vue | 4 +- .../src/pages/admin/proxy-account.vue | 4 +- .../frontend/src/pages/admin/roles.editor.vue | 7 ++- .../frontend/src/pages/admin/roles.role.vue | 5 +- packages/frontend/src/pages/admin/roles.vue | 4 +- .../frontend/src/pages/admin/security.vue | 4 +- .../frontend/src/pages/admin/server-rules.vue | 10 +-- .../frontend/src/pages/admin/settings.vue | 10 +-- packages/frontend/src/pages/ads.vue | 6 +- packages/frontend/src/pages/contact.vue | 16 ++--- .../frontend/src/pages/drive.file.info.vue | 9 +-- packages/frontend/src/pages/favorites.vue | 5 +- .../frontend/src/pages/follow-requests.vue | 5 +- packages/frontend/src/pages/instance-info.vue | 7 ++- packages/frontend/src/pages/invite.vue | 13 ++-- packages/frontend/src/pages/list.vue | 7 ++- .../frontend/src/pages/my-antennas/index.vue | 5 +- .../frontend/src/pages/my-lists/index.vue | 5 +- packages/frontend/src/pages/not-found.vue | 7 ++- packages/frontend/src/pages/page.vue | 9 +-- packages/frontend/src/pages/role.vue | 9 +-- packages/frontend/src/pages/search.note.vue | 9 +-- packages/frontend/src/pages/search.vue | 8 ++- packages/frontend/src/pages/settings/apps.vue | 5 +- .../frontend/src/pages/settings/email.vue | 9 +-- .../frontend/src/pages/settings/index.vue | 5 +- .../src/pages/settings/mute-block.vue | 9 +-- .../frontend/src/pages/settings/theme.vue | 9 +-- packages/frontend/src/pages/timeline.vue | 18 +++--- packages/frontend/src/pages/user/home.vue | 12 ++-- .../frontend/src/pages/user/index.files.vue | 7 ++- .../frontend/src/pages/welcome.entrance.a.vue | 11 ++-- packages/frontend/src/pages/welcome.vue | 4 +- .../frontend/src/scripts/check-permissions.ts | 18 +++--- packages/frontend/src/scripts/clear-cache.ts | 4 +- .../frontend/src/scripts/get-note-menu.ts | 21 +++---- .../frontend/src/scripts/get-user-menu.ts | 6 +- packages/frontend/src/scripts/media-proxy.ts | 53 ---------------- packages/frontend/src/scripts/upload.ts | 22 +++---- .../src/{instance.ts => server-metadata.ts} | 34 +++------- packages/frontend/src/timelines.ts | 12 ++-- packages/frontend/src/ui/_common_/common.ts | 23 +++---- .../src/ui/_common_/navbar-for-mobile.vue | 9 +-- packages/frontend/src/ui/_common_/navbar.vue | 11 ++-- .../src/ui/_common_/statusbar-federation.vue | 9 +-- packages/frontend/src/ui/classic.header.vue | 7 ++- packages/frontend/src/ui/classic.sidebar.vue | 7 ++- packages/frontend/src/ui/deck/tl-column.vue | 7 ++- packages/frontend/src/ui/visitor.vue | 9 +-- .../src/widgets/WidgetBirthdayFollowings.vue | 5 +- .../frontend/src/widgets/WidgetFederation.vue | 9 +-- .../src/widgets/WidgetInstanceCloud.vue | 9 +-- .../src/widgets/WidgetInstanceInfo.vue | 10 +-- .../frontend/src/widgets/WidgetPhotos.vue | 7 ++- packages/frontend/src/widgets/WidgetRss.vue | 5 +- .../frontend/src/widgets/WidgetTimeline.vue | 8 ++- 117 files changed, 587 insertions(+), 568 deletions(-) delete mode 100644 packages/frontend-embed/src/to-be-shared/media-proxy.ts create mode 100644 packages/frontend-shared/js/media-proxy.ts delete mode 100644 packages/frontend/src/scripts/media-proxy.ts rename packages/frontend/src/{instance.ts => server-metadata.ts} (51%) diff --git a/packages/frontend-embed/src/components/EmCustomEmoji.vue b/packages/frontend-embed/src/components/EmCustomEmoji.vue index b4ebf96ebf..fa4f1862c8 100644 --- a/packages/frontend-embed/src/components/EmCustomEmoji.vue +++ b/packages/frontend-embed/src/components/EmCustomEmoji.vue @@ -25,9 +25,10 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { computed, inject, ref } from 'vue'; -import { getProxiedImageUrl } from '@/to-be-shared/media-proxy.js'; import { customEmojisMap } from '@/custom-emojis.js'; +const mediaProxy = inject('mediaProxy'); + const props = defineProps<{ name: string; normal?: boolean; @@ -59,7 +60,7 @@ const url = computed(() => { const proxied = (rawUrl.value.startsWith('/emoji/') || (props.useOriginalSize && isLocal.value)) ? rawUrl.value - : getProxiedImageUrl( + : mediaProxy.getProxiedImageUrl( rawUrl.value, props.useOriginalSize ? undefined : 'emoji', false, diff --git a/packages/frontend-embed/src/components/EmInstanceTicker.vue b/packages/frontend-embed/src/components/EmInstanceTicker.vue index b4998c8894..85b3381085 100644 --- a/packages/frontend-embed/src/components/EmInstanceTicker.vue +++ b/packages/frontend-embed/src/components/EmInstanceTicker.vue @@ -11,26 +11,19 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { computed } from 'vue'; -import { instanceName } from '@/config.js'; -import { instance as Instance } from '@/instance.js'; -import { getProxiedImageUrlNullable } from '@/to-be-shared/media-proxy.js'; +import { computed, inject } from 'vue'; + +const mediaProxy = inject('mediaProxy'); const props = defineProps<{ - instance?: { + instance: { faviconUrl?: string | null name?: string | null themeColor?: string | null } }>(); -// if no instance data is given, this is for the local instance -const instance = props.instance ?? { - name: instanceName, - themeColor: (document.querySelector('meta[name="theme-color-orig"]') as HTMLMetaElement).content, -}; - -const faviconUrl = computed(() => props.instance ? getProxiedImageUrlNullable(props.instance.faviconUrl, 'preview') : getProxiedImageUrlNullable(Instance.iconUrl, 'preview') ?? '/favicon.ico'); +const faviconUrl = computed(() => mediaProxy.getProxiedImageUrlNullable(props.instance.faviconUrl, 'preview')); const themeColor = instance.themeColor ?? '#777777'; diff --git a/packages/frontend-embed/src/components/EmNoteDetailed.vue b/packages/frontend-embed/src/components/EmNoteDetailed.vue index 2afb5d9fa7..cc9be13a57 100644 --- a/packages/frontend-embed/src/components/EmNoteDetailed.vue +++ b/packages/frontend-embed/src/components/EmNoteDetailed.vue @@ -142,7 +142,7 @@ import { userPage } from '@/utils.js'; import { notePage } from '@/utils.js'; import { i18n } from '@/i18n.js'; import { shouldCollapsed } from '@/to-be-shared/collapsed.js'; -import { instance } from '@/instance.js'; +import { instance } from '@/server-metadata.js'; import { url } from '@/config.js'; import EmMfm from '@/components/EmMfm.js'; diff --git a/packages/frontend-embed/src/pages/clip.vue b/packages/frontend-embed/src/pages/clip.vue index 77fe95d883..bb4f8bf7c5 100644 --- a/packages/frontend-embed/src/pages/clip.vue +++ b/packages/frontend-embed/src/pages/clip.vue @@ -48,7 +48,7 @@ import XNotFound from '@/pages/not-found.vue'; import EmTimelineContainer from '@/components/EmTimelineContainer.vue'; import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; -import { instance } from '@/instance.js'; +import { instance } from '@/server-metadata.js'; import { url, instanceName } from '@/config.js'; import { scrollToTop } from '@@/js/scroll.js'; import { isLink } from '@/scripts/is-link.js'; diff --git a/packages/frontend-embed/src/pages/tag.vue b/packages/frontend-embed/src/pages/tag.vue index e27f52c558..4e1b501421 100644 --- a/packages/frontend-embed/src/pages/tag.vue +++ b/packages/frontend-embed/src/pages/tag.vue @@ -45,7 +45,7 @@ import EmNotes from '@/components/EmNotes.vue'; import XNotFound from '@/pages/not-found.vue'; import EmTimelineContainer from '@/components/EmTimelineContainer.vue'; import { i18n } from '@/i18n.js'; -import { instance } from '@/instance.js'; +import { instance } from '@/server-metadata.js'; import { url, instanceName } from '@/config.js'; import { scrollToTop } from '@@/js/scroll.js'; import { isLink } from '@/scripts/is-link.js'; diff --git a/packages/frontend-embed/src/pages/user-timeline.vue b/packages/frontend-embed/src/pages/user-timeline.vue index b1ab77b990..13fbd442a2 100644 --- a/packages/frontend-embed/src/pages/user-timeline.vue +++ b/packages/frontend-embed/src/pages/user-timeline.vue @@ -62,7 +62,7 @@ import XNotFound from '@/pages/not-found.vue'; import EmTimelineContainer from '@/components/EmTimelineContainer.vue'; import { misskeyApi } from '@/misskey-api.js'; import { i18n } from '@/i18n.js'; -import { instance } from '@/instance.js'; +import { instance } from '@/server-metadata.js'; import { url, instanceName } from '@/config.js'; import { defaultEmbedParams } from '@/embed-page.js'; diff --git a/packages/frontend-embed/src/to-be-shared/media-proxy.ts b/packages/frontend-embed/src/to-be-shared/media-proxy.ts deleted file mode 100644 index facd50ac41..0000000000 --- a/packages/frontend-embed/src/to-be-shared/media-proxy.ts +++ /dev/null @@ -1,53 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and misskey-project - * SPDX-License-Identifier: AGPL-3.0-only - */ - -import { query } from '@@/js/url.js'; -import { url } from '@/config.js'; -import { instance } from '@/instance.js'; - -export function getProxiedImageUrl(imageUrl: string, type?: 'preview' | 'emoji' | 'avatar', mustOrigin = false, noFallback = false): string { - const localProxy = `${url}/proxy`; - - if (imageUrl.startsWith(instance.mediaProxy + '/') || imageUrl.startsWith('/proxy/') || imageUrl.startsWith(localProxy + '/')) { - // もう既にproxyっぽそうだったらurlを取り出す - imageUrl = (new URL(imageUrl)).searchParams.get('url') ?? imageUrl; - } - - return `${mustOrigin ? localProxy : instance.mediaProxy}/${ - type === 'preview' ? 'preview.webp' - : 'image.webp' - }?${query({ - url: imageUrl, - ...(!noFallback ? { 'fallback': '1' } : {}), - ...(type ? { [type]: '1' } : {}), - ...(mustOrigin ? { origin: '1' } : {}), - })}`; -} - -export function getProxiedImageUrlNullable(imageUrl: string | null | undefined, type?: 'preview'): string | null { - if (imageUrl == null) return null; - return getProxiedImageUrl(imageUrl, type); -} - -export function getStaticImageUrl(baseUrl: string): string { - const u = baseUrl.startsWith('http') ? new URL(baseUrl) : new URL(baseUrl, url); - - if (u.href.startsWith(`${url}/emoji/`)) { - // もう既にemojiっぽそうだったらsearchParams付けるだけ - u.searchParams.set('static', '1'); - return u.href; - } - - if (u.href.startsWith(instance.mediaProxy + '/')) { - // もう既にproxyっぽそうだったらsearchParams付けるだけ - u.searchParams.set('static', '1'); - return u.href; - } - - return `${instance.mediaProxy}/static.webp?${query({ - url: u.href, - static: '1', - })}`; -} diff --git a/packages/frontend-shared/js/const.ts b/packages/frontend-shared/js/const.ts index 8391fb638c..7f56f673c4 100644 --- a/packages/frontend-shared/js/const.ts +++ b/packages/frontend-shared/js/const.ts @@ -106,10 +106,6 @@ export const ROLE_POLICIES = [ export const CURRENT_STICKY_TOP = 'CURRENT_STICKY_TOP'; export const CURRENT_STICKY_BOTTOM = 'CURRENT_STICKY_BOTTOM'; -export const DEFAULT_SERVER_ERROR_IMAGE_URL = 'https://xn--931a.moe/assets/error.jpg'; -export const DEFAULT_NOT_FOUND_IMAGE_URL = 'https://xn--931a.moe/assets/not-found.jpg'; -export const DEFAULT_INFO_IMAGE_URL = 'https://xn--931a.moe/assets/info.jpg'; - export const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'scale', 'position', 'fg', 'bg', 'border', 'font', 'blur', 'rainbow', 'sparkle', 'rotate', 'ruby', 'unixtime']; export const MFM_PARAMS: Record<typeof MFM_TAGS[number], string[]> = { tada: ['speed=', 'delay='], diff --git a/packages/frontend-shared/js/media-proxy.ts b/packages/frontend-shared/js/media-proxy.ts new file mode 100644 index 0000000000..fcfbac61dc --- /dev/null +++ b/packages/frontend-shared/js/media-proxy.ts @@ -0,0 +1,62 @@ +/* + * SPDX-FileCopyrightText: syuilo and misskey-project + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { query } from '@@/js/url.js'; +import * as Misskey from 'misskey-js'; + +export class MediaProxy { + private serverMetadata: Misskey.entities.MetaDetailed; + private url: string; + + constructor(serverMetadata: Misskey.entities.MetaDetailed, url: string) { + this.serverMetadata = serverMetadata; + this.url = url; + } + + public getProxiedImageUrl(imageUrl: string, type?: 'preview' | 'emoji' | 'avatar', mustOrigin = false, noFallback = false): string { + const localProxy = `${this.url}/proxy`; + + if (imageUrl.startsWith(this.serverMetadata.mediaProxy + '/') || imageUrl.startsWith('/proxy/') || imageUrl.startsWith(localProxy + '/')) { + // もう既にproxyっぽそうだったらurlを取り出す + imageUrl = (new URL(imageUrl)).searchParams.get('url') ?? imageUrl; + } + + return `${mustOrigin ? localProxy : this.serverMetadata.mediaProxy}/${ + type === 'preview' ? 'preview.webp' + : 'image.webp' + }?${query({ + url: imageUrl, + ...(!noFallback ? { 'fallback': '1' } : {}), + ...(type ? { [type]: '1' } : {}), + ...(mustOrigin ? { origin: '1' } : {}), + })}`; + } + + public getProxiedImageUrlNullable(imageUrl: string | null | undefined, type?: 'preview'): string | null { + if (imageUrl == null) return null; + return this.getProxiedImageUrl(imageUrl, type); + } + + public getStaticImageUrl(baseUrl: string): string { + const u = baseUrl.startsWith('http') ? new URL(baseUrl) : new URL(baseUrl, this.url); + + if (u.href.startsWith(`${this.url}/emoji/`)) { + // もう既にemojiっぽそうだったらsearchParams付けるだけ + u.searchParams.set('static', '1'); + return u.href; + } + + if (u.href.startsWith(this.serverMetadata.mediaProxy + '/')) { + // もう既にproxyっぽそうだったらsearchParams付けるだけ + u.searchParams.set('static', '1'); + return u.href; + } + + return `${this.serverMetadata.mediaProxy}/static.webp?${query({ + url: u.href, + static: '1', + })}`; + } +} diff --git a/packages/frontend/src/boot/common.ts b/packages/frontend/src/boot/common.ts index 19d30f64ce..ad5a7c384f 100644 --- a/packages/frontend/src/boot/common.ts +++ b/packages/frontend/src/boot/common.ts @@ -5,16 +5,17 @@ import { computed, watch, version as vueVersion, App } from 'vue'; import { compareVersions } from 'compare-versions'; +import { MediaProxy } from '@@/js/media-proxy.js'; import widgets from '@/widgets/index.js'; import directives from '@/directives/index.js'; import components from '@/components/index.js'; -import { version, lang, updateLocale, locale } from '@/config.js'; +import { version, lang, updateLocale, locale, url } from '@/config.js'; import { applyTheme } from '@/scripts/theme.js'; import { isDeviceDarkmode } from '@/scripts/is-device-darkmode.js'; import { updateI18n } from '@/i18n.js'; import { $i, refreshAccount, login } from '@/account.js'; import { defaultStore, ColdDeviceStorage } from '@/store.js'; -import { fetchInstance, instance } from '@/instance.js'; +import { fetchServerMetadata } from '@/server-metadata.js'; import { deviceKind } from '@/scripts/device-kind.js'; import { reloadChannel } from '@/scripts/unison-reload.js'; import { getUrlWithoutLoginId } from '@/scripts/login-id.js'; @@ -119,11 +120,7 @@ export async function common(createVue: () => App<Element>) { await defaultStore.ready; await deckStore.ready; - const fetchInstanceMetaPromise = fetchInstance(); - - fetchInstanceMetaPromise.then(() => { - miLocalStorage.setItem('v', instance.version); - }); + const serverMetadata = await fetchServerMetadata(); //#region loginId const params = new URLSearchParams(location.search); @@ -178,19 +175,17 @@ export async function common(createVue: () => App<Element>) { }); //#endregion - fetchInstanceMetaPromise.then(() => { - if (defaultStore.state.themeInitial) { - if (instance.defaultLightTheme != null) ColdDeviceStorage.set('lightTheme', JSON.parse(instance.defaultLightTheme)); - if (instance.defaultDarkTheme != null) ColdDeviceStorage.set('darkTheme', JSON.parse(instance.defaultDarkTheme)); - defaultStore.set('themeInitial', false); + if (defaultStore.state.themeInitial) { + if (serverMetadata.defaultLightTheme != null) ColdDeviceStorage.set('lightTheme', JSON.parse(serverMetadata.defaultLightTheme)); + if (serverMetadata.defaultDarkTheme != null) ColdDeviceStorage.set('darkTheme', JSON.parse(serverMetadata.defaultDarkTheme)); + defaultStore.set('themeInitial', false); + } else { + if (defaultStore.state.darkMode) { + applyTheme(darkTheme.value); } else { - if (defaultStore.state.darkMode) { - applyTheme(darkTheme.value); - } else { - applyTheme(lightTheme.value); - } + applyTheme(lightTheme.value); } - }); + } watch(defaultStore.reactiveState.useBlurEffectForModal, v => { document.documentElement.style.setProperty('--modalBgFilter', v ? 'blur(4px)' : 'none'); @@ -239,6 +234,8 @@ export async function common(createVue: () => App<Element>) { } catch (err) { /* empty */ } const app = createVue(); + app.provide('serverMetadata', serverMetadata); + app.provide('mediaProxy', new MediaProxy(serverMetadata, url)); setupRouter(app, createMainRouter); diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts index b31281dcf2..2241177405 100644 --- a/packages/frontend/src/boot/main-boot.ts +++ b/packages/frontend/src/boot/main-boot.ts @@ -12,7 +12,6 @@ import { alert, confirm, popup, post, toast } from '@/os.js'; import { useStream } from '@/stream.js'; import * as sound from '@/scripts/sound.js'; import { $i, signout, updateAccount } from '@/account.js'; -import { instance } from '@/instance.js'; import { ColdDeviceStorage, defaultStore } from '@/store.js'; import { reactionPicker } from '@/scripts/reaction-picker.js'; import { miLocalStorage } from '@/local-storage.js'; @@ -23,6 +22,7 @@ import { emojiPicker } from '@/scripts/emoji-picker.js'; import { mainRouter } from '@/router/main.js'; import { type Keymap, makeHotkey } from '@/scripts/hotkey.js'; import { addCustomEmoji, removeCustomEmojis, updateCustomEmojis } from '@/custom-emojis.js'; +import { fetchServerMetadata } from '@/server-metadata.js'; export async function mainBoot() { const { isClientUpdated } = await common(() => createApp( @@ -267,8 +267,9 @@ export async function mainBoot() { } } + const serverMetadata = await fetchServerMetadata(); const modifiedVersionMustProminentlyOfferInAgplV3Section13Read = miLocalStorage.getItem('modifiedVersionMustProminentlyOfferInAgplV3Section13Read'); - if (modifiedVersionMustProminentlyOfferInAgplV3Section13Read !== 'true' && instance.repositoryUrl !== 'https://github.com/misskey-dev/misskey') { + if (modifiedVersionMustProminentlyOfferInAgplV3Section13Read !== 'true' && serverMetadata.repositoryUrl !== 'https://github.com/misskey-dev/misskey') { const { dispose } = popup(defineAsyncComponent(() => import('@/components/MkSourceCodeAvailablePopup.vue')), {}, { closed: () => dispose(), }); diff --git a/packages/frontend/src/components/MkChannelList.vue b/packages/frontend/src/components/MkChannelList.vue index 2850ecca16..725bdc53e0 100644 --- a/packages/frontend/src/components/MkChannelList.vue +++ b/packages/frontend/src/components/MkChannelList.vue @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkPagination :pagination="pagination"> <template #empty> <div class="_fullinfo"> - <img :src="infoImageUrl" class="_ghost"/> + <img v-if="serverMetadata.infoImageUrl" :src="serverMetadata.infoImageUrl" class="_ghost"/> <div>{{ i18n.ts.notFound }}</div> </div> </template> @@ -19,10 +19,12 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> +import { inject } from 'vue'; import MkChannelPreview from '@/components/MkChannelPreview.vue'; import MkPagination, { Paging } from '@/components/MkPagination.vue'; import { i18n } from '@/i18n.js'; -import { infoImageUrl } from '@/instance.js'; + +const serverMetadata = inject('serverMetadata'); const props = withDefaults(defineProps<{ pagination: Paging; diff --git a/packages/frontend/src/components/MkCropperDialog.vue b/packages/frontend/src/components/MkCropperDialog.vue index 54f6f39c9d..9ff8d32c2b 100644 --- a/packages/frontend/src/components/MkCropperDialog.vue +++ b/packages/frontend/src/components/MkCropperDialog.vue @@ -31,7 +31,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onMounted, shallowRef, ref } from 'vue'; +import { onMounted, shallowRef, ref, inject } from 'vue'; import * as Misskey from 'misskey-js'; import Cropper from 'cropperjs'; import tinycolor from 'tinycolor2'; @@ -41,7 +41,8 @@ import { $i } from '@/account.js'; import { defaultStore } from '@/store.js'; import { apiUrl } from '@/config.js'; import { i18n } from '@/i18n.js'; -import { getProxiedImageUrl } from '@/scripts/media-proxy.js'; + +const mediaProxy = inject('mediaProxy'); const emit = defineEmits<{ (ev: 'ok', cropped: Misskey.entities.DriveFile): void; @@ -55,7 +56,7 @@ const props = defineProps<{ uploadFolder?: string | null; }>(); -const imgUrl = getProxiedImageUrl(props.file.url, undefined, true); +const imgUrl = mediaProxy.getProxiedImageUrl(props.file.url, undefined, true); const dialogEl = shallowRef<InstanceType<typeof MkModalWindow>>(); const imgEl = shallowRef<HTMLImageElement>(); let cropper: Cropper | null = null; diff --git a/packages/frontend/src/components/MkDonation.stories.impl.ts b/packages/frontend/src/components/MkDonation.stories.impl.ts index 27d6b7df6c..94fca7a8fe 100644 --- a/packages/frontend/src/components/MkDonation.stories.impl.ts +++ b/packages/frontend/src/components/MkDonation.stories.impl.ts @@ -7,7 +7,7 @@ import { action } from '@storybook/addon-actions'; import { StoryObj } from '@storybook/vue3'; import { onBeforeUnmount } from 'vue'; import MkDonation from './MkDonation.vue'; -import { instance } from '@/instance.js'; +import { instance } from '@/server-metadata.js'; export const Default = { render(args) { return { diff --git a/packages/frontend/src/components/MkDonation.vue b/packages/frontend/src/components/MkDonation.vue index 434fc81582..c81fa979d3 100644 --- a/packages/frontend/src/components/MkDonation.vue +++ b/packages/frontend/src/components/MkDonation.vue @@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div :class="$style.text"> <I18n :src="i18n.ts.pleaseDonate" tag="span"> <template #host> - {{ instance.name ?? host }} + {{ serverMetadata.name ?? host }} </template> </I18n> <div style="margin-top: 0.2em;"> @@ -36,13 +36,15 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> +import { inject } from 'vue'; import MkButton from '@/components/MkButton.vue'; import MkLink from '@/components/MkLink.vue'; import { host } from '@/config.js'; import { i18n } from '@/i18n.js'; import * as os from '@/os.js'; import { miLocalStorage } from '@/local-storage.js'; -import { instance } from '@/instance.js'; + +const serverMetadata = inject('serverMetadata'); const emit = defineEmits<{ (ev: 'closed'): void; diff --git a/packages/frontend/src/components/MkFeaturedPhotos.vue b/packages/frontend/src/components/MkFeaturedPhotos.vue index c42c692db0..a3ef4e63ae 100644 --- a/packages/frontend/src/components/MkFeaturedPhotos.vue +++ b/packages/frontend/src/components/MkFeaturedPhotos.vue @@ -4,11 +4,12 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div v-if="instance" :class="$style.root" :style="{ backgroundImage: `url(${ instance.backgroundImageUrl })` }"></div> +<div :class="$style.root" :style="{ backgroundImage: `url(${ serverMetadata.backgroundImageUrl })` }"></div> </template> <script lang="ts" setup> -import { instance } from '@/instance.js'; +import { inject } from 'vue'; +const serverMetadata = inject('serverMetadata'); </script> <style lang="scss" module> diff --git a/packages/frontend/src/components/MkForgotPassword.vue b/packages/frontend/src/components/MkForgotPassword.vue index 35112ad45d..0471b23e3b 100644 --- a/packages/frontend/src/components/MkForgotPassword.vue +++ b/packages/frontend/src/components/MkForgotPassword.vue @@ -14,7 +14,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template #header>{{ i18n.ts.forgotPassword }}</template> <MkSpacer :marginMin="20" :marginMax="28"> - <form v-if="instance.enableEmail" @submit.prevent="onSubmit"> + <form v-if="serverMetadata.enableEmail" @submit.prevent="onSubmit"> <div class="_gaps_m"> <MkInput v-model="username" type="text" pattern="^[a-zA-Z0-9_]+$" :spellcheck="false" autofocus required> <template #label>{{ i18n.ts.username }}</template> @@ -39,15 +39,16 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { ref } from 'vue'; +import { inject, ref } from 'vue'; import MkModalWindow from '@/components/MkModalWindow.vue'; import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; import MkInfo from '@/components/MkInfo.vue'; import * as os from '@/os.js'; -import { instance } from '@/instance.js'; import { i18n } from '@/i18n.js'; +const serverMetadata = inject('serverMetadata'); + const emit = defineEmits<{ (ev: 'done'): void; (ev: 'closed'): void; diff --git a/packages/frontend/src/components/MkFormDialog.vue b/packages/frontend/src/components/MkFormDialog.vue index 124f114111..e2dc7c3b3d 100644 --- a/packages/frontend/src/components/MkFormDialog.vue +++ b/packages/frontend/src/components/MkFormDialog.vue @@ -63,7 +63,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> </div> <div v-else class="_fullinfo"> - <img :src="infoImageUrl" class="_ghost"/> + <img v-if="serverMetadata.infoImageUrl" :src="serverMetadata.infoImageUrl" class="_ghost"/> <div>{{ i18n.ts.nothing }}</div> </div> </MkSpacer> @@ -72,6 +72,7 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { reactive, shallowRef } from 'vue'; +import { inject } from 'vue'; import MkInput from './MkInput.vue'; import MkTextarea from './MkTextarea.vue'; import MkSwitch from './MkSwitch.vue'; @@ -83,7 +84,7 @@ import XFile from './MkFormDialog.file.vue'; import type { Form } from '@/scripts/form.js'; import MkModalWindow from '@/components/MkModalWindow.vue'; import { i18n } from '@/i18n.js'; -import { infoImageUrl } from '@/instance.js'; +const serverMetadata = inject('serverMetadata'); const props = defineProps<{ title: string; diff --git a/packages/frontend/src/components/MkInstanceCardMini.vue b/packages/frontend/src/components/MkInstanceCardMini.vue index 17c974dd04..ab0abb6b0d 100644 --- a/packages/frontend/src/components/MkInstanceCardMini.vue +++ b/packages/frontend/src/components/MkInstanceCardMini.vue @@ -15,11 +15,12 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { ref } from 'vue'; +import { inject, ref } from 'vue'; import * as Misskey from 'misskey-js'; import MkMiniChart from '@/components/MkMiniChart.vue'; import { misskeyApiGet } from '@/scripts/misskey-api.js'; -import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js'; + +const mediaProxy = inject('mediaProxy'); const props = defineProps<{ instance: Misskey.entities.FederationInstance; @@ -34,7 +35,7 @@ misskeyApiGet('charts/instance', { host: props.instance.host, limit: 16 + 1, spa }); function getInstanceIcon(instance): string { - return getProxiedImageUrlNullable(instance.iconUrl, 'preview') ?? getProxiedImageUrlNullable(instance.faviconUrl, 'preview') ?? '/client-assets/dummy.png'; + return mediaProxy.getProxiedImageUrlNullable(instance.iconUrl, 'preview') ?? mediaProxy.getProxiedImageUrlNullable(instance.faviconUrl, 'preview') ?? '/client-assets/dummy.png'; } </script> diff --git a/packages/frontend/src/components/MkInstanceTicker.vue b/packages/frontend/src/components/MkInstanceTicker.vue index 82c82199b5..b783ade5e9 100644 --- a/packages/frontend/src/components/MkInstanceTicker.vue +++ b/packages/frontend/src/components/MkInstanceTicker.vue @@ -11,10 +11,10 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { computed } from 'vue'; +import { computed, inject } from 'vue'; import { instanceName } from '@/config.js'; -import { instance as Instance } from '@/instance.js'; -import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js'; + +const mediaProxy = inject('mediaProxy'); const props = defineProps<{ instance?: { @@ -30,7 +30,7 @@ const instance = props.instance ?? { themeColor: (document.querySelector('meta[name="theme-color-orig"]') as HTMLMetaElement).content, }; -const faviconUrl = computed(() => props.instance ? getProxiedImageUrlNullable(props.instance.faviconUrl, 'preview') : getProxiedImageUrlNullable(Instance.iconUrl, 'preview') ?? '/favicon.ico'); +const faviconUrl = computed(() => props.instance ? mediaProxy.getProxiedImageUrlNullable(props.instance.faviconUrl, 'preview') : mediaProxy.getProxiedImageUrlNullable(Instance.iconUrl, 'preview') ?? '/favicon.ico'); const themeColor = instance.themeColor ?? '#777777'; diff --git a/packages/frontend/src/components/MkLink.vue b/packages/frontend/src/components/MkLink.vue index e842ec2d6e..6ef1036cc7 100644 --- a/packages/frontend/src/components/MkLink.vue +++ b/packages/frontend/src/components/MkLink.vue @@ -15,13 +15,14 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { defineAsyncComponent, ref } from 'vue'; +import { defineAsyncComponent, inject, ref } from 'vue'; import { url as local } from '@/config.js'; import { useTooltip } from '@/scripts/use-tooltip.js'; import * as os from '@/os.js'; -import { isEnabledUrlPreview } from '@/instance.js'; import { MkABehavior } from '@/components/global/MkA.vue'; +const serverMetadata = inject('serverMetadata'); + const props = withDefaults(defineProps<{ url: string; rel?: null | string; @@ -35,7 +36,7 @@ const target = self ? null : '_blank'; const el = ref<HTMLElement | { $el: HTMLElement }>(); -if (isEnabledUrlPreview.value) { +if (serverMetadata.enableUrlPreview) { useTooltip(el, (showing) => { const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkUrlPreviewPopup.vue')), { showing, diff --git a/packages/frontend/src/components/MkMediaImage.vue b/packages/frontend/src/components/MkMediaImage.vue index 0d1409e2c8..0c18c1c694 100644 --- a/packages/frontend/src/components/MkMediaImage.vue +++ b/packages/frontend/src/components/MkMediaImage.vue @@ -51,9 +51,8 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { watch, ref, computed } from 'vue'; +import { watch, ref, computed, inject } from 'vue'; import * as Misskey from 'misskey-js'; -import { getStaticImageUrl } from '@/scripts/media-proxy.js'; import bytes from '@/filters/bytes.js'; import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue'; import { defaultStore } from '@/store.js'; @@ -61,6 +60,8 @@ import { i18n } from '@/i18n.js'; import * as os from '@/os.js'; import { $i, iAmModerator } from '@/account.js'; +const mediaProxy = inject('mediaProxy'); + const props = withDefaults(defineProps<{ image: Misskey.entities.DriveFile; raw?: boolean; @@ -79,7 +80,7 @@ const darkMode = ref<boolean>(defaultStore.state.darkMode); const url = computed(() => (props.raw || defaultStore.state.loadRawImages) ? props.image.url : defaultStore.state.disableShowingAnimatedImages - ? getStaticImageUrl(props.image.url) + ? mediaProxy.getStaticImageUrl(props.image.url) : props.image.thumbnailUrl, ); diff --git a/packages/frontend/src/components/MkMention.vue b/packages/frontend/src/components/MkMention.vue index bfb49a416e..62ec940ffb 100644 --- a/packages/frontend/src/components/MkMention.vue +++ b/packages/frontend/src/components/MkMention.vue @@ -15,14 +15,15 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { toUnicode } from 'punycode'; -import { computed } from 'vue'; +import { computed, inject } from 'vue'; import tinycolor from 'tinycolor2'; import { host as localHost } from '@/config.js'; import { $i } from '@/account.js'; import { defaultStore } from '@/store.js'; -import { getStaticImageUrl } from '@/scripts/media-proxy.js'; import { MkABehavior } from '@/components/global/MkA.vue'; +const mediaProxy = inject('mediaProxy'); + const props = defineProps<{ username: string; host: string; @@ -42,7 +43,7 @@ bg.setAlpha(0.1); const bgCss = bg.toRgbString(); const avatarUrl = computed(() => defaultStore.state.disableShowingAnimatedImages - ? getStaticImageUrl(`/avatar/@${props.username}@${props.host}`) + ? mediaProxy.getStaticImageUrl(`/avatar/@${props.username}@${props.host}`) : `/avatar/@${props.username}@${props.host}`, ); </script> diff --git a/packages/frontend/src/components/MkNote.vue b/packages/frontend/src/components/MkNote.vue index 2927a46977..1465cf0b92 100644 --- a/packages/frontend/src/components/MkNote.vue +++ b/packages/frontend/src/components/MkNote.vue @@ -82,7 +82,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkMediaList ref="galleryEl" :mediaList="appearNote.files"/> </div> <MkPoll v-if="appearNote.poll" :noteId="appearNote.id" :poll="appearNote.poll" :class="$style.poll"/> - <div v-if="isEnabledUrlPreview"> + <div v-if="serverMetadata.enableUrlPreview"> <MkUrlPreview v-for="url in urls" :key="url" :url="url" :compact="true" :detail="false" :class="$style.urlPreview"/> </div> <div v-if="appearNote.renote" :class="$style.quote"><MkNoteSimple :note="appearNote.renote" :class="$style.quoteNote"/></div> @@ -197,11 +197,12 @@ import MkRippleEffect from '@/components/MkRippleEffect.vue'; import { showMovedDialog } from '@/scripts/show-moved-dialog.js'; import { shouldCollapsed } from '@/scripts/collapsed.js'; import { host } from '@/config.js'; -import { isEnabledUrlPreview } from '@/instance.js'; import { type Keymap } from '@/scripts/hotkey.js'; import { focusPrev, focusNext } from '@/scripts/focus.js'; import { getAppearNote } from '@/scripts/get-appear-note.js'; +const serverMetadata = inject('serverMetadata'); + const props = withDefaults(defineProps<{ note: Misskey.entities.Note; pinned?: boolean; @@ -523,7 +524,7 @@ function onContextmenu(ev: MouseEvent): void { ev.preventDefault(); react(); } else { - const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted, currentClip: currentClip?.value }); + const { menu, cleanup } = getNoteMenu({ serverMetadata }, { note: note.value, translating, translation, isDeleted, currentClip: currentClip?.value }); os.contextMenu(menu, ev).then(focus).finally(cleanup); } } @@ -533,7 +534,7 @@ function showMenu(): void { return; } - const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted, currentClip: currentClip?.value }); + const { menu, cleanup } = getNoteMenu({ serverMetadata }, { note: note.value, translating, translation, isDeleted, currentClip: currentClip?.value }); os.popupMenu(menu, menuButton.value).then(focus).finally(cleanup); } diff --git a/packages/frontend/src/components/MkNoteDetailed.vue b/packages/frontend/src/components/MkNoteDetailed.vue index 2b7d2afa04..d1269a177c 100644 --- a/packages/frontend/src/components/MkNoteDetailed.vue +++ b/packages/frontend/src/components/MkNoteDetailed.vue @@ -96,7 +96,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkMediaList ref="galleryEl" :mediaList="appearNote.files"/> </div> <MkPoll v-if="appearNote.poll" ref="pollViewer" :noteId="appearNote.id" :poll="appearNote.poll" :class="$style.poll"/> - <div v-if="isEnabledUrlPreview"> + <div v-if="serverMetadata.enableUrlPreview"> <MkUrlPreview v-for="url in urls" :key="url" :url="url" :compact="true" :detail="true" style="margin-top: 6px;"/> </div> <div v-if="appearNote.renote" :class="$style.quote"><MkNoteSimple :note="appearNote.renote" :class="$style.quoteNote"/></div> @@ -234,10 +234,11 @@ import MkUserCardMini from '@/components/MkUserCardMini.vue'; import MkPagination, { type Paging } from '@/components/MkPagination.vue'; import MkReactionIcon from '@/components/MkReactionIcon.vue'; import MkButton from '@/components/MkButton.vue'; -import { isEnabledUrlPreview } from '@/instance.js'; import { getAppearNote } from '@/scripts/get-appear-note.js'; import { type Keymap } from '@/scripts/hotkey.js'; +const serverMetadata = inject('serverMetadata'); + const props = withDefaults(defineProps<{ note: Misskey.entities.Note; initialTab: string; @@ -483,13 +484,13 @@ function onContextmenu(ev: MouseEvent): void { ev.preventDefault(); react(); } else { - const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted }); + const { menu, cleanup } = getNoteMenu({ serverMetadata }, { note: note.value, translating, translation, isDeleted }); os.contextMenu(menu, ev).then(focus).finally(cleanup); } } function showMenu(): void { - const { menu, cleanup } = getNoteMenu({ note: note.value, translating, translation, isDeleted }); + const { menu, cleanup } = getNoteMenu({ serverMetadata }, { note: note.value, translating, translation, isDeleted }); os.popupMenu(menu, menuButton.value).then(focus).finally(cleanup); } diff --git a/packages/frontend/src/components/MkNotes.vue b/packages/frontend/src/components/MkNotes.vue index 0856c146ba..e7a94005cf 100644 --- a/packages/frontend/src/components/MkNotes.vue +++ b/packages/frontend/src/components/MkNotes.vue @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkPagination ref="pagingComponent" :pagination="pagination" :disableAutoLoad="disableAutoLoad"> <template #empty> <div class="_fullinfo"> - <img :src="infoImageUrl" class="_ghost"/> + <img v-if="serverMetadata.infoImageUrl" :src="serverMetadata.infoImageUrl" class="_ghost"/> <div>{{ i18n.ts.noNotes }}</div> </div> </template> @@ -33,11 +33,12 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { shallowRef } from 'vue'; +import { inject } from 'vue'; import MkNote from '@/components/MkNote.vue'; import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue'; import MkPagination, { Paging } from '@/components/MkPagination.vue'; import { i18n } from '@/i18n.js'; -import { infoImageUrl } from '@/instance.js'; +const serverMetadata = inject('serverMetadata'); const props = defineProps<{ pagination: Paging; diff --git a/packages/frontend/src/components/MkNotification.vue b/packages/frontend/src/components/MkNotification.vue index ee65743574..55cdda7285 100644 --- a/packages/frontend/src/components/MkNotification.vue +++ b/packages/frontend/src/components/MkNotification.vue @@ -148,7 +148,8 @@ import { userPage } from '@/filters/user.js'; import { i18n } from '@/i18n.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; import { signinRequired } from '@/account.js'; -import { infoImageUrl } from '@/instance.js'; +import { inject } from 'vue'; +const serverMetadata = inject('serverMetadata'); const $i = signinRequired(); diff --git a/packages/frontend/src/components/MkNotifications.vue b/packages/frontend/src/components/MkNotifications.vue index 389987338d..26096359f3 100644 --- a/packages/frontend/src/components/MkNotifications.vue +++ b/packages/frontend/src/components/MkNotifications.vue @@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkPagination ref="pagingComponent" :pagination="pagination"> <template #empty> <div class="_fullinfo"> - <img :src="infoImageUrl" class="_ghost"/> + <img v-if="serverMetadata.infoImageUrl" :src="serverMetadata.infoImageUrl" class="_ghost"/> <div>{{ i18n.ts.noNotifications }}</div> </div> </template> @@ -32,7 +32,8 @@ import MkNote from '@/components/MkNote.vue'; import { useStream } from '@/stream.js'; import { i18n } from '@/i18n.js'; import { notificationTypes } from '@/const.js'; -import { infoImageUrl } from '@/instance.js'; +import { inject } from 'vue'; +const serverMetadata = inject('serverMetadata'); import { defaultStore } from '@/store.js'; import MkPullToRefresh from '@/components/MkPullToRefresh.vue'; import * as Misskey from 'misskey-js'; diff --git a/packages/frontend/src/components/MkPagination.vue b/packages/frontend/src/components/MkPagination.vue index d30f915c55..6731a56fb4 100644 --- a/packages/frontend/src/components/MkPagination.vue +++ b/packages/frontend/src/components/MkPagination.vue @@ -18,7 +18,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-else-if="empty" key="_empty_" class="empty"> <slot name="empty"> <div class="_fullinfo"> - <img :src="infoImageUrl" class="_ghost"/> + <img v-if="serverMetadata.infoImageUrl" :src="serverMetadata.infoImageUrl" class="_ghost"/> <div>{{ i18n.ts.nothing }}</div> </div> </slot> @@ -90,7 +90,8 @@ function concatMapWithArray(map: MisskeyEntityMap, entities: MisskeyEntity[]): M </script> <script lang="ts" setup> -import { infoImageUrl } from '@/instance.js'; +import { inject } from 'vue'; +const serverMetadata = inject('serverMetadata'); import MkButton from '@/components/MkButton.vue'; const props = withDefaults(defineProps<{ diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue index df251d9192..56bd05366f 100644 --- a/packages/frontend/src/components/MkPostForm.vue +++ b/packages/frontend/src/components/MkPostForm.vue @@ -120,7 +120,6 @@ import { selectFiles } from '@/scripts/select-file.js'; import { defaultStore, notePostInterruptors, postFormActions } from '@/store.js'; import MkInfo from '@/components/MkInfo.vue'; import { i18n } from '@/i18n.js'; -import { instance } from '@/instance.js'; import { signinRequired, notesCount, incNotesCount, getAccounts, openAccountMenu as openAccountMenu_ } from '@/account.js'; import { uploadFile } from '@/scripts/upload.js'; import { deepClone } from '@/scripts/clone.js'; @@ -130,6 +129,8 @@ import { claimAchievement } from '@/scripts/achievements.js'; import { emojiPicker } from '@/scripts/emoji-picker.js'; import { mfmFunctionPicker } from '@/scripts/mfm-function-picker.js'; +const serverMetadata = inject('serverMetadata'); + const $i = signinRequired(); const modal = inject('modal'); @@ -249,7 +250,7 @@ const textLength = computed((): number => { }); const maxTextLength = computed((): number => { - return instance ? instance.maxNoteTextLength : 1000; + return serverMetadata.maxNoteTextLength; }); const canPost = computed((): boolean => { diff --git a/packages/frontend/src/components/MkPushNotificationAllowButton.vue b/packages/frontend/src/components/MkPushNotificationAllowButton.vue index 5e42df4795..66b76bbd35 100644 --- a/packages/frontend/src/components/MkPushNotificationAllowButton.vue +++ b/packages/frontend/src/components/MkPushNotificationAllowButton.vue @@ -41,14 +41,15 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script setup lang="ts"> -import { ref } from 'vue'; +import { inject, ref } from 'vue'; import { $i, getAccounts } from '@/account.js'; import MkButton from '@/components/MkButton.vue'; -import { instance } from '@/instance.js'; import { apiWithDialog, promiseDialog } from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; +const serverMetadata = inject('serverMetadata'); + defineProps<{ primary?: boolean; gradate?: boolean; @@ -72,12 +73,12 @@ const pushSubscription = ref<PushSubscription | null>(null); const pushRegistrationInServer = ref<{ state?: string; key?: string; userId: string; endpoint: string; sendReadMessage: boolean; } | undefined>(); function subscribe() { - if (!registration.value || !supported.value || !instance.swPublickey) return; + if (!registration.value || !supported.value || !serverMetadata.swPublickey) return; // SEE: https://developer.mozilla.org/en-US/docs/Web/API/PushManager/subscribe#Parameters return promiseDialog(registration.value.pushManager.subscribe({ userVisibleOnly: true, - applicationServerKey: urlBase64ToUint8Array(instance.swPublickey), + applicationServerKey: urlBase64ToUint8Array(serverMetadata.swPublickey), }) .then(async subscription => { pushSubscription.value = subscription; @@ -156,7 +157,7 @@ if (navigator.serviceWorker == null) { pushSubscription.value = await registration.value.pushManager.getSubscription(); - if (instance.swPublickey && ('PushManager' in window) && $i && $i.token) { + if (serverMetadata.swPublickey && ('PushManager' in window) && $i && $i.token) { supported.value = true; if (pushSubscription.value) { diff --git a/packages/frontend/src/components/MkSignupDialog.form.vue b/packages/frontend/src/components/MkSignupDialog.form.vue index 5f08e416c1..3ce8e78fec 100644 --- a/packages/frontend/src/components/MkSignupDialog.form.vue +++ b/packages/frontend/src/components/MkSignupDialog.form.vue @@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <MkSpacer :marginMin="20" :marginMax="32"> <form class="_gaps_m" autocomplete="new-password" @submit.prevent="onSubmit"> - <MkInput v-if="instance.disableRegistration" v-model="invitationCode" type="text" :spellcheck="false" required> + <MkInput v-if="serverMetadata.disableRegistration" v-model="invitationCode" type="text" :spellcheck="false" required> <template #label>{{ i18n.ts.invitationCode }}</template> <template #prefix><i class="ti ti-key"></i></template> </MkInput> @@ -29,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only <span v-else-if="usernameState === 'max-range'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.tooLong }}</span> </template> </MkInput> - <MkInput v-if="instance.emailRequiredForSignup" v-model="email" :debounce="true" type="email" :spellcheck="false" required data-cy-signup-email @update:modelValue="onChangeEmail"> + <MkInput v-if="serverMetadata.emailRequiredForSignup" v-model="email" :debounce="true" type="email" :spellcheck="false" required data-cy-signup-email @update:modelValue="onChangeEmail"> <template #label>{{ i18n.ts.emailAddress }} <div v-tooltip:dialog="i18n.ts._signup.emailAddressInfo" class="_button _help"><i class="ti ti-help-circle"></i></div></template> <template #prefix><i class="ti ti-mail"></i></template> <template #caption> @@ -62,10 +62,10 @@ SPDX-License-Identifier: AGPL-3.0-only <span v-if="passwordRetypeState == 'not-match'" style="color: var(--error)"><i class="ti ti-alert-triangle ti-fw"></i> {{ i18n.ts.passwordNotMatched }}</span> </template> </MkInput> - <MkCaptcha v-if="instance.enableHcaptcha" ref="hcaptcha" v-model="hCaptchaResponse" :class="$style.captcha" provider="hcaptcha" :sitekey="instance.hcaptchaSiteKey"/> - <MkCaptcha v-if="instance.enableMcaptcha" ref="mcaptcha" v-model="mCaptchaResponse" :class="$style.captcha" provider="mcaptcha" :sitekey="instance.mcaptchaSiteKey" :instanceUrl="instance.mcaptchaInstanceUrl"/> - <MkCaptcha v-if="instance.enableRecaptcha" ref="recaptcha" v-model="reCaptchaResponse" :class="$style.captcha" provider="recaptcha" :sitekey="instance.recaptchaSiteKey"/> - <MkCaptcha v-if="instance.enableTurnstile" ref="turnstile" v-model="turnstileResponse" :class="$style.captcha" provider="turnstile" :sitekey="instance.turnstileSiteKey"/> + <MkCaptcha v-if="serverMetadata.enableHcaptcha" ref="hcaptcha" v-model="hCaptchaResponse" :class="$style.captcha" provider="hcaptcha" :sitekey="serverMetadata.hcaptchaSiteKey"/> + <MkCaptcha v-if="serverMetadata.enableMcaptcha" ref="mcaptcha" v-model="mCaptchaResponse" :class="$style.captcha" provider="mcaptcha" :sitekey="serverMetadata.mcaptchaSiteKey" :instanceUrl="serverMetadata.mcaptchaInstanceUrl"/> + <MkCaptcha v-if="serverMetadata.enableRecaptcha" ref="recaptcha" v-model="reCaptchaResponse" :class="$style.captcha" provider="recaptcha" :sitekey="serverMetadata.recaptchaSiteKey"/> + <MkCaptcha v-if="serverMetadata.enableTurnstile" ref="turnstile" v-model="turnstileResponse" :class="$style.captcha" provider="turnstile" :sitekey="serverMetadata.turnstileSiteKey"/> <MkButton type="submit" :disabled="shouldDisableSubmitting" large gradate rounded data-cy-signup-submit style="margin: 0 auto;"> <template v-if="submitting"> <MkLoading :em="true" :colored="false"/> @@ -78,7 +78,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { ref, computed } from 'vue'; +import { ref, computed, inject } from 'vue'; import { toUnicode } from 'punycode/'; import * as Misskey from 'misskey-js'; import MkButton from './MkButton.vue'; @@ -88,9 +88,10 @@ import * as config from '@/config.js'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; import { login } from '@/account.js'; -import { instance } from '@/instance.js'; import { i18n } from '@/i18n.js'; +const serverMetadata = inject('serverMetadata'); + const props = withDefaults(defineProps<{ autoSet?: boolean; }>(), { @@ -127,11 +128,11 @@ const emailAbortController = ref<null | AbortController>(null); const shouldDisableSubmitting = computed((): boolean => { return submitting.value || - instance.enableHcaptcha && !hCaptchaResponse.value || - instance.enableMcaptcha && !mCaptchaResponse.value || - instance.enableRecaptcha && !reCaptchaResponse.value || - instance.enableTurnstile && !turnstileResponse.value || - instance.emailRequiredForSignup && emailState.value !== 'ok' || + serverMetadata.enableHcaptcha && !hCaptchaResponse.value || + serverMetadata.enableMcaptcha && !mCaptchaResponse.value || + serverMetadata.enableRecaptcha && !reCaptchaResponse.value || + serverMetadata.enableTurnstile && !turnstileResponse.value || + serverMetadata.emailRequiredForSignup && emailState.value !== 'ok' || usernameState.value !== 'ok' || passwordRetypeState.value !== 'match'; }); @@ -260,7 +261,7 @@ async function onSubmit(): Promise<void> { 'g-recaptcha-response': reCaptchaResponse.value, 'turnstile-response': turnstileResponse.value, }); - if (instance.emailRequiredForSignup) { + if (serverMetadata.emailRequiredForSignup) { os.alert({ type: 'success', title: i18n.ts._signup.almostThere, diff --git a/packages/frontend/src/components/MkSignupDialog.rules.stories.impl.ts b/packages/frontend/src/components/MkSignupDialog.rules.stories.impl.ts index 9df3ec0c30..fb2cdb4d30 100644 --- a/packages/frontend/src/components/MkSignupDialog.rules.stories.impl.ts +++ b/packages/frontend/src/components/MkSignupDialog.rules.stories.impl.ts @@ -9,7 +9,7 @@ import { StoryObj } from '@storybook/vue3'; import { onBeforeUnmount } from 'vue'; import MkSignupServerRules from './MkSignupDialog.rules.vue'; import { i18n } from '@/i18n.js'; -import { instance } from '@/instance.js'; +import { instance } from '@/server-metadata.js'; export const Empty = { render(args) { return { diff --git a/packages/frontend/src/components/MkSignupDialog.rules.vue b/packages/frontend/src/components/MkSignupDialog.rules.vue index 59a3651cd4..9ab6a5cce3 100644 --- a/packages/frontend/src/components/MkSignupDialog.rules.vue +++ b/packages/frontend/src/components/MkSignupDialog.rules.vue @@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> <MkSpacer :marginMin="20" :marginMax="28"> <div class="_gaps_m"> - <div v-if="instance.disableRegistration"> + <div v-if="serverMetadata.disableRegistration"> <MkInfo warn>{{ i18n.ts.invitationRequiredToRegister }}</MkInfo> </div> @@ -24,7 +24,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template #suffix><i v-if="agreeServerRules" class="ti ti-check" style="color: var(--success)"></i></template> <ol class="_gaps_s" :class="$style.rules"> - <li v-for="item in instance.serverRules" :class="$style.rule"><div :class="$style.ruleText" v-html="item"></div></li> + <li v-for="item in serverMetadata.serverRules" :class="$style.rule"><div :class="$style.ruleText" v-html="item"></div></li> </ol> <MkSwitch :modelValue="agreeServerRules" style="margin-top: 16px;" @update:modelValue="updateAgreeServerRules">{{ i18n.ts.agree }}</MkSwitch> @@ -34,8 +34,8 @@ SPDX-License-Identifier: AGPL-3.0-only <template #label>{{ tosPrivacyPolicyLabel }}</template> <template #suffix><i v-if="agreeTosAndPrivacyPolicy" class="ti ti-check" style="color: var(--success)"></i></template> <div class="_gaps_s"> - <div v-if="availableTos"><a :href="instance.tosUrl ?? undefined" class="_link" target="_blank">{{ i18n.ts.termsOfService }} <i class="ti ti-external-link"></i></a></div> - <div v-if="availablePrivacyPolicy"><a :href="instance.privacyPolicyUrl ?? undefined" class="_link" target="_blank">{{ i18n.ts.privacyPolicy }} <i class="ti ti-external-link"></i></a></div> + <div v-if="availableTos"><a :href="serverMetadata.tosUrl ?? undefined" class="_link" target="_blank">{{ i18n.ts.termsOfService }} <i class="ti ti-external-link"></i></a></div> + <div v-if="availablePrivacyPolicy"><a :href="serverMetadata.privacyPolicyUrl ?? undefined" class="_link" target="_blank">{{ i18n.ts.privacyPolicy }} <i class="ti ti-external-link"></i></a></div> </div> <MkSwitch :modelValue="agreeTosAndPrivacyPolicy" style="margin-top: 16px;" @update:modelValue="updateAgreeTosAndPrivacyPolicy">{{ i18n.ts.agree }}</MkSwitch> @@ -62,8 +62,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { computed, ref } from 'vue'; -import { instance } from '@/instance.js'; +import { computed, inject, ref } from 'vue'; import { i18n } from '@/i18n.js'; import MkButton from '@/components/MkButton.vue'; import MkFolder from '@/components/MkFolder.vue'; @@ -71,9 +70,11 @@ import MkSwitch from '@/components/MkSwitch.vue'; import MkInfo from '@/components/MkInfo.vue'; import * as os from '@/os.js'; -const availableServerRules = instance.serverRules.length > 0; -const availableTos = instance.tosUrl != null && instance.tosUrl !== ''; -const availablePrivacyPolicy = instance.privacyPolicyUrl != null && instance.privacyPolicyUrl !== ''; +const serverMetadata = inject('serverMetadata'); + +const availableServerRules = serverMetadata.serverRules.length > 0; +const availableTos = serverMetadata.tosUrl != null && serverMetadata.tosUrl !== ''; +const availablePrivacyPolicy = serverMetadata.privacyPolicyUrl != null && serverMetadata.privacyPolicyUrl !== ''; const agreeServerRules = ref(false); const agreeTosAndPrivacyPolicy = ref(false); diff --git a/packages/frontend/src/components/MkSourceCodeAvailablePopup.vue b/packages/frontend/src/components/MkSourceCodeAvailablePopup.vue index 80f3a6709c..634df9878c 100644 --- a/packages/frontend/src/components/MkSourceCodeAvailablePopup.vue +++ b/packages/frontend/src/components/MkSourceCodeAvailablePopup.vue @@ -15,14 +15,14 @@ SPDX-License-Identifier: AGPL-3.0-only <div :class="$style.title"> <I18n :src="i18n.ts.aboutX" tag="span"> <template #x> - {{ instance.name ?? host }} + {{ serverMetadata.name ?? host }} </template> </I18n> </div> <div :class="$style.text"> <I18n :src="i18n.ts._aboutMisskey.thisIsModifiedVersion" tag="span"> <template #name> - {{ instance.name ?? host }} + {{ serverMetadata.name ?? host }} </template> </I18n> <I18n :src="i18n.ts.correspondingSourceIsAvailable" tag="span"> @@ -40,13 +40,15 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> +import { inject } from 'vue'; import MkButton from '@/components/MkButton.vue'; import { host } from '@/config.js'; import { i18n } from '@/i18n.js'; -import { instance } from '@/instance.js'; import { miLocalStorage } from '@/local-storage.js'; import * as os from '@/os.js'; +const serverMetadata = inject('serverMetadata'); + const emit = defineEmits<{ (ev: 'closed'): void; }>(); diff --git a/packages/frontend/src/components/MkTimeline.vue b/packages/frontend/src/components/MkTimeline.vue index ca87316bf7..0ee3fd18b3 100644 --- a/packages/frontend/src/components/MkTimeline.vue +++ b/packages/frontend/src/components/MkTimeline.vue @@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { computed, watch, onUnmounted, provide, ref, shallowRef } from 'vue'; +import { computed, watch, onUnmounted, provide, ref, shallowRef, inject } from 'vue'; import * as Misskey from 'misskey-js'; import type { BasicTimelineType } from '@/timelines.js'; import MkNotes from '@/components/MkNotes.vue'; @@ -25,10 +25,11 @@ import MkPullToRefresh from '@/components/MkPullToRefresh.vue'; import { useStream } from '@/stream.js'; import * as sound from '@/scripts/sound.js'; import { $i } from '@/account.js'; -import { instance } from '@/instance.js'; import { defaultStore } from '@/store.js'; import { Paging } from '@/components/MkPagination.vue'; +const serverMetadata = inject('serverMetadata'); + const props = withDefaults(defineProps<{ src: BasicTimelineType | 'mentions' | 'directs' | 'list' | 'antenna' | 'channel' | 'role'; list?: string; diff --git a/packages/frontend/src/components/MkTutorialDialog.vue b/packages/frontend/src/components/MkTutorialDialog.vue index 9adc8d466c..4015c9d69f 100644 --- a/packages/frontend/src/components/MkTutorialDialog.vue +++ b/packages/frontend/src/components/MkTutorialDialog.vue @@ -133,7 +133,7 @@ SPDX-License-Identifier: AGPL-3.0-only <a href="https://misskey-hub.net/docs/for-users/" target="_blank" class="_link">{{ i18n.ts.help }}</a> </template> </I18n> - <div>{{ i18n.tsx._initialAccountSetting.haveFun({ name: instance.name ?? host }) }}</div> + <div>{{ i18n.tsx._initialAccountSetting.haveFun({ name: serverMetadata.name ?? host }) }}</div> <div class="_buttonsCenter" style="margin-top: 16px;"> <MkButton v-if="initialPage !== 4" rounded @click="page--"><i class="ti ti-arrow-left"></i> {{ i18n.ts.goBack }}</MkButton> <MkButton rounded primary gradate @click="close(false)">{{ i18n.ts.close }}</MkButton> @@ -148,7 +148,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { ref, shallowRef, watch } from 'vue'; +import { inject, ref, shallowRef, watch } from 'vue'; import MkModalWindow from '@/components/MkModalWindow.vue'; import MkButton from '@/components/MkButton.vue'; import XNote from '@/components/MkTutorialDialog.Note.vue'; @@ -157,11 +157,12 @@ import XPostNote from '@/components/MkTutorialDialog.PostNote.vue'; import XSensitive from '@/components/MkTutorialDialog.Sensitive.vue'; import MkAnimBg from '@/components/MkAnimBg.vue'; import { i18n } from '@/i18n.js'; -import { instance } from '@/instance.js'; import { host } from '@/config.js'; import { claimAchievement } from '@/scripts/achievements.js'; import * as os from '@/os.js'; +const serverMetadata = inject('serverMetadata'); + const props = defineProps<{ initialPage?: number; }>(); diff --git a/packages/frontend/src/components/MkUserInfo.vue b/packages/frontend/src/components/MkUserInfo.vue index f0b9606590..826a6fc3ea 100644 --- a/packages/frontend/src/components/MkUserInfo.vue +++ b/packages/frontend/src/components/MkUserInfo.vue @@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div class="_panel" :class="$style.root"> - <div :class="$style.banner" :style="user.bannerUrl ? `background-image: url(${defaultStore.state.disableShowingAnimatedImages ? getStaticImageUrl(user.bannerUrl) : user.bannerUrl})` : ''"></div> + <div :class="$style.banner" :style="user.bannerUrl ? `background-image: url(${defaultStore.state.disableShowingAnimatedImages ? mediaProxy.getStaticImageUrl(user.bannerUrl) : user.bannerUrl})` : ''"></div> <MkAvatar :class="$style.avatar" :user="user" indicator/> <div :class="$style.title"> <MkA :class="$style.name" :to="userPage(user)"><MkUserName :user="user" :nowrap="false"/></MkA> @@ -35,15 +35,17 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import * as Misskey from 'misskey-js'; +import { inject } from 'vue'; import MkFollowButton from '@/components/MkFollowButton.vue'; import number from '@/filters/number.js'; import { userPage } from '@/filters/user.js'; import { i18n } from '@/i18n.js'; import { $i } from '@/account.js'; import { isFollowingVisibleForMe, isFollowersVisibleForMe } from '@/scripts/isFfVisibleForMe.js'; -import { getStaticImageUrl } from '@/scripts/media-proxy.js'; import { defaultStore } from '@/store.js'; +const mediaProxy = inject('mediaProxy'); + defineProps<{ user: Misskey.entities.UserDetailed; }>(); diff --git a/packages/frontend/src/components/MkUserList.vue b/packages/frontend/src/components/MkUserList.vue index 17a9254d01..903e48ed40 100644 --- a/packages/frontend/src/components/MkUserList.vue +++ b/packages/frontend/src/components/MkUserList.vue @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkPagination :pagination="pagination"> <template #empty> <div class="_fullinfo"> - <img :src="infoImageUrl" class="_ghost"/> + <img v-if="serverMetadata.infoImageUrl" :src="serverMetadata.infoImageUrl" class="_ghost"/> <div>{{ i18n.ts.noUsers }}</div> </div> </template> @@ -21,10 +21,12 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> +import { inject } from 'vue'; import MkUserInfo from '@/components/MkUserInfo.vue'; import MkPagination, { Paging } from '@/components/MkPagination.vue'; import { i18n } from '@/i18n.js'; -import { infoImageUrl } from '@/instance.js'; + +const serverMetadata = inject('serverMetadata'); const props = withDefaults(defineProps<{ pagination: Paging; diff --git a/packages/frontend/src/components/MkUserPopup.vue b/packages/frontend/src/components/MkUserPopup.vue index ea1241002e..b5b2df520a 100644 --- a/packages/frontend/src/components/MkUserPopup.vue +++ b/packages/frontend/src/components/MkUserPopup.vue @@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only > <div v-if="showing" :class="$style.root" class="_popup _shadow" :style="{ zIndex, top: top + 'px', left: left + 'px' }" @mouseover="() => { emit('mouseover'); }" @mouseleave="() => { emit('mouseleave'); }"> <div v-if="user != null"> - <div :class="$style.banner" :style="user.bannerUrl ? `background-image: url(${defaultStore.state.disableShowingAnimatedImages ? getStaticImageUrl(user.bannerUrl) : user.bannerUrl})` : ''"> + <div :class="$style.banner" :style="user.bannerUrl ? `background-image: url(${defaultStore.state.disableShowingAnimatedImages ? mediaProxy.getStaticImageUrl(user.bannerUrl) : user.bannerUrl})` : ''"> <span v-if="$i && $i.id != user.id && user.isFollowed" :class="$style.followed">{{ i18n.ts.followsYou }}</span> </div> <svg viewBox="0 0 128 128" :class="$style.avatarBack"> @@ -55,7 +55,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onMounted, ref } from 'vue'; +import { inject, onMounted, ref } from 'vue'; import * as Misskey from 'misskey-js'; import MkFollowButton from '@/components/MkFollowButton.vue'; import { userPage } from '@/filters/user.js'; @@ -67,7 +67,9 @@ import { i18n } from '@/i18n.js'; import { defaultStore } from '@/store.js'; import { $i } from '@/account.js'; import { isFollowingVisibleForMe, isFollowersVisibleForMe } from '@/scripts/isFfVisibleForMe.js'; -import { getStaticImageUrl } from '@/scripts/media-proxy.js'; + +const serverMetadata = inject('serverMetadata'); +const mediaProxy = inject('mediaProxy'); const props = defineProps<{ showing: boolean; @@ -88,7 +90,7 @@ const left = ref(0); function showMenu(ev: MouseEvent) { if (user.value == null) return; - const { menu, cleanup } = getUserMenu(user.value); + const { menu, cleanup } = getUserMenu({ serverMetadata }, user.value); os.popupMenu(menu, ev.currentTarget ?? ev.target).finally(cleanup); } diff --git a/packages/frontend/src/components/MkUserSetupDialog.vue b/packages/frontend/src/components/MkUserSetupDialog.vue index 514350c930..69f3dc1b37 100644 --- a/packages/frontend/src/components/MkUserSetupDialog.vue +++ b/packages/frontend/src/components/MkUserSetupDialog.vue @@ -93,7 +93,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div class="_gaps" style="text-align: center;"> <i class="ti ti-bell-ringing-2" style="display: block; margin: auto; font-size: 3em; color: var(--accent);"></i> <div style="font-size: 120%;">{{ i18n.ts.pushNotification }}</div> - <div style="padding: 0 16px;">{{ i18n.tsx._initialAccountSetting.pushNotificationDescription({ name: instance.name ?? host }) }}</div> + <div style="padding: 0 16px;">{{ i18n.tsx._initialAccountSetting.pushNotificationDescription({ name: serverMetadata.name ?? host }) }}</div> <MkPushNotificationAllowButton primary showOnlyToRegister style="margin: 0 auto;"/> <div class="_buttonsCenter" style="margin-top: 16px;"> <MkButton rounded data-cy-user-setup-back @click="page--"><i class="ti ti-arrow-left"></i> {{ i18n.ts.goBack }}</MkButton> @@ -110,7 +110,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div class="_gaps" style="text-align: center;"> <i class="ti ti-check" style="display: block; margin: auto; font-size: 3em; color: var(--accent);"></i> <div style="font-size: 120%;">{{ i18n.ts._initialAccountSetting.initialAccountSettingCompleted }}</div> - <div>{{ i18n.tsx._initialAccountSetting.youCanContinueTutorial({ name: instance.name ?? host }) }}</div> + <div>{{ i18n.tsx._initialAccountSetting.youCanContinueTutorial({ name: serverMetadata.name ?? host }) }}</div> <div class="_buttonsCenter" style="margin-top: 16px;"> <MkButton rounded primary gradate data-cy-user-setup-continue @click="launchTutorial()">{{ i18n.ts._initialAccountSetting.startTutorial }} <i class="ti ti-arrow-right"></i></MkButton> </div> @@ -128,7 +128,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { ref, shallowRef, watch, nextTick, defineAsyncComponent } from 'vue'; +import { ref, shallowRef, watch, nextTick, defineAsyncComponent, inject } from 'vue'; import MkModalWindow from '@/components/MkModalWindow.vue'; import MkButton from '@/components/MkButton.vue'; import XProfile from '@/components/MkUserSetupDialog.Profile.vue'; @@ -136,19 +136,19 @@ import XFollow from '@/components/MkUserSetupDialog.Follow.vue'; import XPrivacy from '@/components/MkUserSetupDialog.Privacy.vue'; import MkAnimBg from '@/components/MkAnimBg.vue'; import { i18n } from '@/i18n.js'; -import { instance } from '@/instance.js'; import { host } from '@/config.js'; import MkPushNotificationAllowButton from '@/components/MkPushNotificationAllowButton.vue'; import { defaultStore } from '@/store.js'; import * as os from '@/os.js'; +const serverMetadata = inject('serverMetadata'); + const emit = defineEmits<{ (ev: 'closed'): void; }>(); const dialog = shallowRef<InstanceType<typeof MkModalWindow>>(); - -// eslint-disable-next-line vue/no-setup-props-reactivity-loss + const page = ref(defaultStore.state.accountSetupWizard); watch(page, () => { diff --git a/packages/frontend/src/components/MkVisitorDashboard.vue b/packages/frontend/src/components/MkVisitorDashboard.vue index 445780eca7..2655acc848 100644 --- a/packages/frontend/src/components/MkVisitorDashboard.vue +++ b/packages/frontend/src/components/MkVisitorDashboard.vue @@ -6,19 +6,19 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div v-if="instance" :class="$style.root"> <div :class="[$style.main, $style.panel]"> - <img :src="instance.iconUrl || '/favicon.ico'" alt="" :class="$style.mainIcon"/> + <img :src="serverMetadata.iconUrl || '/favicon.ico'" alt="" :class="$style.mainIcon"/> <button class="_button _acrylic" :class="$style.mainMenu" @click="showMenu"><i class="ti ti-dots"></i></button> <div :class="$style.mainFg"> <h1 :class="$style.mainTitle"> <!-- 背景色によってはロゴが見えなくなるのでとりあえず無効に --> - <!-- <img class="logo" v-if="instance.logoImageUrl" :src="instance.logoImageUrl"><span v-else class="text">{{ instanceName }}</span> --> + <!-- <img class="logo" v-if="serverMetadata.logoImageUrl" :src="serverMetadata.logoImageUrl"><span v-else class="text">{{ instanceName }}</span> --> <span>{{ instanceName }}</span> </h1> <div :class="$style.mainAbout"> <!-- eslint-disable-next-line vue/no-v-html --> - <div v-html="instance.description || i18n.ts.headlineMisskey"></div> + <div v-html="serverMetadata.description || i18n.ts.headlineMisskey"></div> </div> - <div v-if="instance.disableRegistration" :class="$style.mainWarn"> + <div v-if="serverMetadata.disableRegistration" :class="$style.mainWarn"> <MkInfo warn>{{ i18n.ts.invitationRequiredToRegister }}</MkInfo> </div> <div class="_gaps_s" :class="$style.mainActions"> @@ -38,7 +38,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div :class="$style.statsItemCount"><MkNumber :value="stats.originalNotesCount"/></div> </div> </div> - <div v-if="instance.policies.ltlAvailable" :class="[$style.tl, $style.panel]"> + <div v-if="serverMetadata.policies.ltlAvailable" :class="[$style.tl, $style.panel]"> <div :class="$style.tlHeader">{{ i18n.ts.letsLookAtTimeline }}</div> <div :class="$style.tlBody"> <MkTimeline src="local"/> @@ -51,7 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { ref } from 'vue'; +import { inject, ref } from 'vue'; import * as Misskey from 'misskey-js'; import XSigninDialog from '@/components/MkSigninDialog.vue'; import XSignupDialog from '@/components/MkSignupDialog.vue'; @@ -62,11 +62,11 @@ import { instanceName } from '@/config.js'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; -import { instance } from '@/instance.js'; import MkNumber from '@/components/MkNumber.vue'; import XActiveUsersChart from '@/components/MkVisitorDashboard.ActiveUsersChart.vue'; import { openInstanceMenu } from '@/ui/_common_/common.js'; -import type { MenuItem } from '@/types/menu.js'; + +const serverMetadata = inject('serverMetadata'); const stats = ref<Misskey.entities.StatsResponse | null>(null); diff --git a/packages/frontend/src/components/global/MkAd.vue b/packages/frontend/src/components/global/MkAd.vue index bdaa8a809f..c6a0c4a320 100644 --- a/packages/frontend/src/components/global/MkAd.vue +++ b/packages/frontend/src/components/global/MkAd.vue @@ -42,16 +42,17 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { ref, computed } from 'vue'; +import { ref, computed, inject } from 'vue'; import { i18n } from '@/i18n.js'; -import { instance } from '@/instance.js'; import { url as local, host } from '@/config.js'; import MkButton from '@/components/MkButton.vue'; import { defaultStore } from '@/store.js'; import * as os from '@/os.js'; import { $i } from '@/account.js'; -type Ad = (typeof instance)['ads'][number]; +const serverMetadata = inject('serverMetadata'); + +type Ad = (typeof serverMetadata)['ads'][number]; const props = defineProps<{ prefer: string[]; @@ -68,7 +69,7 @@ const choseAd = (): Ad | null => { return props.specify; } - const allAds = instance.ads.map(ad => defaultStore.state.mutedAds.includes(ad.id) ? { + const allAds = serverMetadata.ads.map(ad => defaultStore.state.mutedAds.includes(ad.id) ? { ...ad, ratio: 0, } : ad); diff --git a/packages/frontend/src/components/global/MkAvatar.vue b/packages/frontend/src/components/global/MkAvatar.vue index 35c07bc80c..018cb68280 100644 --- a/packages/frontend/src/components/global/MkAvatar.vue +++ b/packages/frontend/src/components/global/MkAvatar.vue @@ -40,16 +40,17 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { watch, ref, computed } from 'vue'; +import { watch, ref, computed, inject } from 'vue'; import * as Misskey from 'misskey-js'; import { extractAvgColorFromBlurhash } from '@@/js/extract-avg-color-from-blurhash.js'; import MkImgWithBlurhash from '../MkImgWithBlurhash.vue'; import MkA from './MkA.vue'; -import { getStaticImageUrl } from '@/scripts/media-proxy.js'; import { acct, userPage } from '@/filters/user.js'; import MkUserOnlineIndicator from '@/components/MkUserOnlineIndicator.vue'; import { defaultStore } from '@/store.js'; +const mediaProxy = inject('mediaProxy'); + const animation = ref(defaultStore.state.animation); const squareAvatars = ref(defaultStore.state.squareAvatars); const useBlurEffect = ref(defaultStore.state.useBlurEffect); @@ -83,7 +84,7 @@ const bound = computed(() => props.link const url = computed(() => { if (props.user.avatarUrl == null) return null; - if (defaultStore.state.disableShowingAnimatedImages || defaultStore.state.dataSaver.avatar) return getStaticImageUrl(props.user.avatarUrl); + if (defaultStore.state.disableShowingAnimatedImages || defaultStore.state.dataSaver.avatar) return mediaProxy.getStaticImageUrl(props.user.avatarUrl); return props.user.avatarUrl; }); @@ -93,7 +94,7 @@ function onClick(ev: MouseEvent): void { } function getDecorationUrl(decoration: Omit<Misskey.entities.UserDetailed['avatarDecorations'][number], 'id'>) { - if (defaultStore.state.disableShowingAnimatedImages || defaultStore.state.dataSaver.avatar) return getStaticImageUrl(decoration.url); + if (defaultStore.state.disableShowingAnimatedImages || defaultStore.state.dataSaver.avatar) return mediaProxy.getStaticImageUrl(decoration.url); return decoration.url; } diff --git a/packages/frontend/src/components/global/MkCustomEmoji.vue b/packages/frontend/src/components/global/MkCustomEmoji.vue index dff56cd7f0..82e999ddf9 100644 --- a/packages/frontend/src/components/global/MkCustomEmoji.vue +++ b/packages/frontend/src/components/global/MkCustomEmoji.vue @@ -26,7 +26,6 @@ SPDX-License-Identifier: AGPL-3.0-only <script lang="ts" setup> import { computed, inject, ref } from 'vue'; -import { getProxiedImageUrl, getStaticImageUrl } from '@/scripts/media-proxy.js'; import { defaultStore } from '@/store.js'; import { customEmojisMap } from '@/custom-emojis.js'; import * as os from '@/os.js'; @@ -36,6 +35,8 @@ import * as sound from '@/scripts/sound.js'; import { i18n } from '@/i18n.js'; import MkCustomEmojiDetailedDialog from '@/components/MkCustomEmojiDetailedDialog.vue'; +const mediaProxy = inject('mediaProxy'); + const props = defineProps<{ name: string; normal?: boolean; @@ -69,14 +70,14 @@ const url = computed(() => { const proxied = (rawUrl.value.startsWith('/emoji/') || (props.useOriginalSize && isLocal.value)) ? rawUrl.value - : getProxiedImageUrl( + : mediaProxy.getProxiedImageUrl( rawUrl.value, props.useOriginalSize ? undefined : 'emoji', false, true, ); return defaultStore.reactiveState.disableShowingAnimatedImages.value - ? getStaticImageUrl(proxied) + ? mediaProxy.getStaticImageUrl(proxied) : proxied; }); diff --git a/packages/frontend/src/components/global/MkError.vue b/packages/frontend/src/components/global/MkError.vue index c594cc752b..6404bea38e 100644 --- a/packages/frontend/src/components/global/MkError.vue +++ b/packages/frontend/src/components/global/MkError.vue @@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <Transition :name="defaultStore.state.animation ? '_transition_zoom' : ''" appear> <div :class="$style.root"> - <img :class="$style.img" :src="serverErrorImageUrl" class="_ghost"/> + <img v-if="serverMetadata.serverErrorImageUrl" :class="$style.img" :src="serverMetadata.serverErrorImageUrl" class="_ghost"/> <p :class="$style.text"><i class="ti ti-alert-triangle"></i> {{ i18n.ts.somethingHappened }}</p> <MkButton :class="$style.button" @click="() => emit('retry')">{{ i18n.ts.retry }}</MkButton> </div> @@ -14,10 +14,12 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> +import { inject } from 'vue'; import MkButton from '@/components/MkButton.vue'; import { i18n } from '@/i18n.js'; import { defaultStore } from '@/store.js'; -import { serverErrorImageUrl } from '@/instance.js'; + +const serverMetadata = inject('serverMetadata'); const emit = defineEmits<{ (ev: 'retry'): void; diff --git a/packages/frontend/src/components/global/MkUrl.vue b/packages/frontend/src/components/global/MkUrl.vue index 8f4e3b853a..a476742553 100644 --- a/packages/frontend/src/components/global/MkUrl.vue +++ b/packages/frontend/src/components/global/MkUrl.vue @@ -25,14 +25,15 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { defineAsyncComponent, ref } from 'vue'; +import { defineAsyncComponent, inject, ref } from 'vue'; import { toUnicode as decodePunycode } from 'punycode/'; import { url as local } from '@/config.js'; import * as os from '@/os.js'; import { useTooltip } from '@/scripts/use-tooltip.js'; -import { isEnabledUrlPreview } from '@/instance.js'; import { MkABehavior } from '@/components/global/MkA.vue'; +const serverMetadata = inject('serverMetadata'); + function safeURIDecode(str: string): string { try { return decodeURIComponent(str); @@ -55,7 +56,7 @@ const url = new URL(props.url); if (!['http:', 'https:'].includes(url.protocol)) throw new Error('invalid url'); const el = ref(); -if (props.showUrlPreview && isEnabledUrlPreview.value) { +if (props.showUrlPreview && serverMetadata.enableUrlPreview) { useTooltip(el, (showing) => { const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkUrlPreviewPopup.vue')), { showing, diff --git a/packages/frontend/src/components/page/page.text.vue b/packages/frontend/src/components/page/page.text.vue index e0c7956f6e..edbdba8df9 100644 --- a/packages/frontend/src/components/page/page.text.vue +++ b/packages/frontend/src/components/page/page.text.vue @@ -6,18 +6,19 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div class="_gaps" :class="$style.textRoot"> <Mfm :text="block.text ?? ''" :isNote="false"/> - <div v-if="isEnabledUrlPreview" class="_gaps_s"> + <div v-if="serverMetadata.enableUrlPreview" class="_gaps_s"> <MkUrlPreview v-for="url in urls" :key="url" :url="url"/> </div> </div> </template> <script lang="ts" setup> -import { defineAsyncComponent } from 'vue'; +import { defineAsyncComponent, inject } from 'vue'; import * as mfm from 'mfm-js'; import * as Misskey from 'misskey-js'; import { extractUrlFromMfm } from '@/scripts/extract-url-from-mfm.js'; -import { isEnabledUrlPreview } from '@/instance.js'; + +const serverMetadata = inject('serverMetadata'); const MkUrlPreview = defineAsyncComponent(() => import('@/components/MkUrlPreview.vue')); diff --git a/packages/frontend/src/const.ts b/packages/frontend/src/const.ts index e135bc69a0..7f56f673c4 100644 --- a/packages/frontend/src/const.ts +++ b/packages/frontend/src/const.ts @@ -106,10 +106,6 @@ export const ROLE_POLICIES = [ export const CURRENT_STICKY_TOP = 'CURRENT_STICKY_TOP'; export const CURRENT_STICKY_BOTTOM = 'CURRENT_STICKY_BOTTOM'; -export const DEFAULT_SERVER_ERROR_IMAGE_URL = 'https://xn--931a.moe/assets/error.jpg'; -export const DEFAULT_NOT_FOUND_IMAGE_URL = 'https://xn--931a.moe/assets/not-found.jpg'; -export const DEFAULT_INFO_IMAGE_URL = 'https://xn--931a.moe/assets/info.jpg'; - export const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'scale', 'position', 'fg', 'bg', 'border', 'font', 'blur', 'rainbow', 'sparkle', 'rotate', 'ruby', 'unixtime']; export const MFM_PARAMS: Record<typeof MFM_TAGS[number], string[]> = { tada: ['speed=', 'delay='], @@ -127,7 +123,7 @@ export const MFM_PARAMS: Record<typeof MFM_TAGS[number], string[]> = { position: ['x=', 'y='], fg: ['color='], bg: ['color='], - border: ['width=', 'style=', 'color=', 'radius=', 'noclip'], + border: ['width=', 'style=', 'color=', 'radius=', 'noclip'], font: ['serif', 'monospace', 'cursive', 'fantasy', 'emoji', 'math'], blur: [], rainbow: ['speed=', 'delay='], diff --git a/packages/frontend/src/local-storage.ts b/packages/frontend/src/local-storage.ts index 59ca455362..195629e2af 100644 --- a/packages/frontend/src/local-storage.ts +++ b/packages/frontend/src/local-storage.ts @@ -4,7 +4,6 @@ */ export type Keys = - 'v' | 'lastVersion' | 'instance' | 'instanceCachedAt' | diff --git a/packages/frontend/src/pages/_error_.vue b/packages/frontend/src/pages/_error_.vue index c04f399c6d..e156d103ab 100644 --- a/packages/frontend/src/pages/_error_.vue +++ b/packages/frontend/src/pages/_error_.vue @@ -7,7 +7,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkLoading v-if="!loaded"/> <Transition :name="defaultStore.state.animation ? '_transition_zoom' : ''" appear> <div v-show="loaded" :class="$style.root"> - <img :src="serverErrorImageUrl" class="_ghost" :class="$style.img"/> + <img v-if="serverMetadata.serverErrorImageUrl" :src="serverMetadata.serverErrorImageUrl" class="_ghost" :class="$style.img"/> <div class="_gaps"> <div><b><i class="ti ti-alert-triangle"></i> {{ i18n.ts.pageLoadError }}</b></div> <div v-if="meta && (version === meta.version)">{{ i18n.ts.pageLoadErrorDescription }}</div> @@ -25,7 +25,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { ref, computed } from 'vue'; +import { ref, computed, inject } from 'vue'; import * as Misskey from 'misskey-js'; import MkButton from '@/components/MkButton.vue'; import MkLink from '@/components/MkLink.vue'; @@ -36,7 +36,8 @@ import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { miLocalStorage } from '@/local-storage.js'; import { defaultStore } from '@/store.js'; -import { serverErrorImageUrl } from '@/instance.js'; + +const serverMetadata = inject('serverMetadata'); const props = withDefaults(defineProps<{ error?: Error; diff --git a/packages/frontend/src/pages/about-misskey.vue b/packages/frontend/src/pages/about-misskey.vue index a16c1eeacc..f855e77f34 100644 --- a/packages/frontend/src/pages/about-misskey.vue +++ b/packages/frontend/src/pages/about-misskey.vue @@ -46,21 +46,21 @@ SPDX-License-Identifier: AGPL-3.0-only </FormLink> </div> </FormSection> - <FormSection v-if="instance.repositoryUrl !== 'https://github.com/misskey-dev/misskey'"> + <FormSection v-if="serverMetadata.repositoryUrl !== 'https://github.com/misskey-dev/misskey'"> <div class="_gaps_s"> <MkInfo> - {{ i18n.tsx._aboutMisskey.thisIsModifiedVersion({ name: instance.name }) }} + {{ i18n.tsx._aboutMisskey.thisIsModifiedVersion({ name: serverMetadata.name }) }} </MkInfo> - <FormLink v-if="instance.repositoryUrl" :to="instance.repositoryUrl" external> + <FormLink v-if="serverMetadata.repositoryUrl" :to="serverMetadata.repositoryUrl" external> <template #icon><i class="ti ti-code"></i></template> {{ i18n.ts._aboutMisskey.source }} </FormLink> - <FormLink v-if="instance.providesTarball" :to="`/tarball/misskey-${version}.tar.gz`" external> + <FormLink v-if="serverMetadata.providesTarball" :to="`/tarball/misskey-${version}.tar.gz`" external> <template #icon><i class="ti ti-download"></i></template> {{ i18n.ts._aboutMisskey.source }} <template #suffix>Tarball</template> </FormLink> - <MkInfo v-if="!instance.repositoryUrl && !instance.providesTarball" warn> + <MkInfo v-if="!serverMetadata.repositoryUrl && !serverMetadata.providesTarball" warn> {{ i18n.ts.sourceCodeIsNotYetProvided }} </MkInfo> </div> @@ -131,7 +131,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { nextTick, onBeforeUnmount, ref, shallowRef, computed } from 'vue'; +import { nextTick, onBeforeUnmount, ref, shallowRef, computed, inject } from 'vue'; import { version } from '@/config.js'; import FormLink from '@/components/form/link.vue'; import FormSection from '@/components/form/section.vue'; @@ -139,13 +139,14 @@ import MkButton from '@/components/MkButton.vue'; import MkInfo from '@/components/MkInfo.vue'; import { physics } from '@/scripts/physics.js'; import { i18n } from '@/i18n.js'; -import { instance } from '@/instance.js'; import { defaultStore } from '@/store.js'; import * as os from '@/os.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { claimAchievement, claimedAchievements } from '@/scripts/achievements.js'; import { $i } from '@/account.js'; +const serverMetadata = inject('serverMetadata'); + const patronsWithIcon = [{ name: 'カイヤン', icon: 'https://assets.misskey-hub.net/patrons/a2820716883e408cb87773e377ce7c8d.jpg', diff --git a/packages/frontend/src/pages/about.overview.vue b/packages/frontend/src/pages/about.overview.vue index 84419b3bef..0e05a9d808 100644 --- a/packages/frontend/src/pages/about.overview.vue +++ b/packages/frontend/src/pages/about.overview.vue @@ -5,18 +5,18 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div class="_gaps_m"> - <div :class="$style.banner" :style="{ backgroundImage: `url(${ instance.bannerUrl })` }"> + <div :class="$style.banner" :style="{ backgroundImage: `url(${ serverMetadata.bannerUrl })` }"> <div style="overflow: clip;"> - <img :src="instance.iconUrl ?? instance.faviconUrl ?? '/favicon.ico'" alt="" :class="$style.bannerIcon"/> + <img :src="serverMetadata.iconUrl ?? serverMetadata.faviconUrl ?? '/favicon.ico'" alt="" :class="$style.bannerIcon"/> <div :class="$style.bannerName"> - <b>{{ instance.name ?? host }}</b> + <b>{{ serverMetadata.name ?? host }}</b> </div> </div> </div> <MkKeyValue> <template #key>{{ i18n.ts.description }}</template> - <template #value><div v-html="instance.description"></div></template> + <template #value><div v-html="serverMetadata.description"></div></template> </MkKeyValue> <FormSection> @@ -25,13 +25,13 @@ SPDX-License-Identifier: AGPL-3.0-only <template #key>Misskey</template> <template #value>{{ version }}</template> </MkKeyValue> - <div v-html="i18n.tsx.poweredByMisskeyDescription({ name: instance.name ?? host })"> + <div v-html="i18n.tsx.poweredByMisskeyDescription({ name: serverMetadata.name ?? host })"> </div> <FormLink to="/about-misskey"> <template #icon><i class="ti ti-info-circle"></i></template> {{ i18n.ts.aboutMisskey }} </FormLink> - <FormLink v-if="instance.repositoryUrl || instance.providesTarball" :to="instance.repositoryUrl || `/tarball/misskey-${version}.tar.gz`" external> + <FormLink v-if="serverMetadata.repositoryUrl || serverMetadata.providesTarball" :to="serverMetadata.repositoryUrl || `/tarball/misskey-${version}.tar.gz`" external> <template #icon><i class="ti ti-code"></i></template> {{ i18n.ts.sourceCode }} </FormLink> @@ -44,51 +44,51 @@ SPDX-License-Identifier: AGPL-3.0-only <FormSection> <div class="_gaps_m"> <FormSplit> - <MkKeyValue :copy="instance.maintainerName"> + <MkKeyValue :copy="serverMetadata.maintainerName"> <template #key>{{ i18n.ts.administrator }}</template> <template #value> - <template v-if="instance.maintainerName">{{ instance.maintainerName }}</template> + <template v-if="serverMetadata.maintainerName">{{ serverMetadata.maintainerName }}</template> <span v-else style="opacity: 0.7;">({{ i18n.ts.none }})</span> </template> </MkKeyValue> - <MkKeyValue :copy="instance.maintainerEmail"> + <MkKeyValue :copy="serverMetadata.maintainerEmail"> <template #key>{{ i18n.ts.contact }}</template> <template #value> - <template v-if="instance.maintainerEmail">{{ instance.maintainerEmail }}</template> + <template v-if="serverMetadata.maintainerEmail">{{ serverMetadata.maintainerEmail }}</template> <span v-else style="opacity: 0.7;">({{ i18n.ts.none }})</span> </template> </MkKeyValue> <MkKeyValue> <template #key>{{ i18n.ts.inquiry }}</template> <template #value> - <MkLink v-if="instance.inquiryUrl" :url="instance.inquiryUrl" target="_blank">{{ instance.inquiryUrl }}</MkLink> + <MkLink v-if="serverMetadata.inquiryUrl" :url="serverMetadata.inquiryUrl" target="_blank">{{ serverMetadata.inquiryUrl }}</MkLink> <span v-else style="opacity: 0.7;">({{ i18n.ts.none }})</span> </template> </MkKeyValue> </FormSplit> <div class="_gaps_s"> - <FormLink v-if="instance.impressumUrl" :to="instance.impressumUrl" external> + <FormLink v-if="serverMetadata.impressumUrl" :to="serverMetadata.impressumUrl" external> <template #icon><i class="ti ti-user-shield"></i></template> <template #default>{{ i18n.ts.impressum }}</template> </FormLink> - <MkFolder v-if="instance.serverRules.length > 0"> + <MkFolder v-if="serverMetadata.serverRules.length > 0"> <template #icon><i class="ti ti-checkup-list"></i></template> <template #label>{{ i18n.ts.serverRules }}</template> <ol class="_gaps_s" :class="$style.rules"> - <li v-for="item in instance.serverRules" :key="item" :class="$style.rule"> + <li v-for="item in serverMetadata.serverRules" :key="item" :class="$style.rule"> <div :class="$style.ruleText" v-html="item"></div> </li> </ol> </MkFolder> - <FormLink v-if="instance.tosUrl" :to="instance.tosUrl" external> + <FormLink v-if="serverMetadata.tosUrl" :to="serverMetadata.tosUrl" external> <template #icon><i class="ti ti-license"></i></template> <template #default>{{ i18n.ts.termsOfService }}</template> </FormLink> - <FormLink v-if="instance.privacyPolicyUrl" :to="instance.privacyPolicyUrl" external> + <FormLink v-if="serverMetadata.privacyPolicyUrl" :to="serverMetadata.privacyPolicyUrl" external> <template #icon><i class="ti ti-shield-lock"></i></template> <template #default>{{ i18n.ts.privacyPolicy }}</template> </FormLink> - <FormLink v-if="instance.feedbackUrl" :to="instance.feedbackUrl" external> + <FormLink v-if="serverMetadata.feedbackUrl" :to="serverMetadata.feedbackUrl" external> <template #icon><i class="ti ti-message"></i></template> <template #default>{{ i18n.ts.feedback }}</template> </FormLink> @@ -126,9 +126,9 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> +import { inject } from 'vue'; import { host, version } from '@/config.js'; import { i18n } from '@/i18n.js'; -import { instance } from '@/instance.js'; import number from '@/filters/number.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; import FormLink from '@/components/form/link.vue'; @@ -139,6 +139,8 @@ import MkFolder from '@/components/MkFolder.vue'; import MkKeyValue from '@/components/MkKeyValue.vue'; import MkLink from '@/components/MkLink.vue'; +const serverMetadata = inject('serverMetadata'); + const initStats = () => misskeyApi('stats', {}); </script> diff --git a/packages/frontend/src/pages/admin/bot-protection.vue b/packages/frontend/src/pages/admin/bot-protection.vue index 73c5e1919f..c477039735 100644 --- a/packages/frontend/src/pages/admin/bot-protection.vue +++ b/packages/frontend/src/pages/admin/bot-protection.vue @@ -92,7 +92,7 @@ import FormSuspense from '@/components/form/suspense.vue'; import FormSlot from '@/components/form/slot.vue'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; -import { fetchInstance } from '@/instance.js'; +import { fetchServerMetadata } from '@/server-metadata.js'; import { i18n } from '@/i18n.js'; const MkCaptcha = defineAsyncComponent(() => import('@/components/MkCaptcha.vue')); @@ -142,7 +142,7 @@ function save() { turnstileSiteKey: turnstileSiteKey.value, turnstileSecretKey: turnstileSecretKey.value, }).then(() => { - fetchInstance(true); + fetchServerMetadata(true); }); } </script> diff --git a/packages/frontend/src/pages/admin/branding.vue b/packages/frontend/src/pages/admin/branding.vue index fe1b7c561d..ed614781ca 100644 --- a/packages/frontend/src/pages/admin/branding.vue +++ b/packages/frontend/src/pages/admin/branding.vue @@ -112,7 +112,7 @@ import MkTextarea from '@/components/MkTextarea.vue'; import FormSuspense from '@/components/form/suspense.vue'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; -import { instance, fetchInstance } from '@/instance.js'; +import { instance, fetchServerMetadata } from '@/server-metadata.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import MkButton from '@/components/MkButton.vue'; @@ -169,7 +169,7 @@ function save() { feedbackUrl: feedbackUrl.value === '' ? null : feedbackUrl.value, manifestJsonOverride: manifestJsonOverride.value === '' ? '{}' : JSON.stringify(JSON5.parse(manifestJsonOverride.value)), }).then(() => { - fetchInstance(true); + fetchServerMetadata(true); }); } diff --git a/packages/frontend/src/pages/admin/email-settings.vue b/packages/frontend/src/pages/admin/email-settings.vue index 4a858887f3..bb72dfa72d 100644 --- a/packages/frontend/src/pages/admin/email-settings.vue +++ b/packages/frontend/src/pages/admin/email-settings.vue @@ -74,7 +74,7 @@ import FormSplit from '@/components/form/split.vue'; import FormSection from '@/components/form/section.vue'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; -import { fetchInstance, instance } from '@/instance.js'; +import { fetchServerMetadata } from '@/server-metadata.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import MkButton from '@/components/MkButton.vue'; @@ -99,10 +99,11 @@ async function init() { } async function testEmail() { + const serverMetadata = await fetchServerMetadata(); const { canceled, result: destination } = await os.inputText({ title: i18n.ts.destination, type: 'email', - default: instance.maintainerEmail ?? '', + default: serverMetadata.maintainerEmail ?? '', placeholder: 'test@example.com', minLength: 1, }); @@ -124,7 +125,7 @@ function save() { smtpUser: smtpUser.value, smtpPass: smtpPass.value, }).then(() => { - fetchInstance(true); + fetchServerMetadata(true); }); } diff --git a/packages/frontend/src/pages/admin/external-services.vue b/packages/frontend/src/pages/admin/external-services.vue index e0b82eb02e..2842c63b18 100644 --- a/packages/frontend/src/pages/admin/external-services.vue +++ b/packages/frontend/src/pages/admin/external-services.vue @@ -43,7 +43,7 @@ import FormSuspense from '@/components/form/suspense.vue'; import FormSection from '@/components/form/section.vue'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; -import { fetchInstance } from '@/instance.js'; +import { fetchServerMetadata } from '@/server-metadata.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; @@ -61,7 +61,7 @@ function save() { deeplAuthKey: deeplAuthKey.value, deeplIsPro: deeplIsPro.value, }).then(() => { - fetchInstance(true); + fetchServerMetadata(true); }); } diff --git a/packages/frontend/src/pages/admin/index.vue b/packages/frontend/src/pages/admin/index.vue index 40dec55deb..0c8a2439cf 100644 --- a/packages/frontend/src/pages/admin/index.vue +++ b/packages/frontend/src/pages/admin/index.vue @@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSpacer :contentMax="700" :marginMin="16"> <div class="lxpfedzu _gaps"> <div class="banner"> - <img :src="instance.iconUrl || '/favicon.ico'" alt="" class="icon"/> + <img :src="serverMetadata.iconUrl || '/favicon.ico'" alt="" class="icon"/> </div> <div class="_gaps_s"> @@ -31,11 +31,10 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onActivated, onMounted, onUnmounted, provide, watch, ref, computed } from 'vue'; +import { onActivated, onMounted, onUnmounted, provide, watch, ref, computed, inject } from 'vue'; import { i18n } from '@/i18n.js'; import MkSuperMenu from '@/components/MkSuperMenu.vue'; import MkInfo from '@/components/MkInfo.vue'; -import { instance } from '@/instance.js'; import { lookup } from '@/scripts/lookup.js'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; @@ -43,6 +42,8 @@ import { lookupUser, lookupUserByEmail, lookupFile } from '@/scripts/admin-looku import { PageMetadata, definePageMetadata, provideMetadataReceiver, provideReactiveMetadata } from '@/scripts/page-metadata.js'; import { useRouter } from '@/router/supplier.js'; +const serverMetadata = inject('serverMetadata'); + const isEmpty = (x: string | null) => x == null || x === ''; const router = useRouter(); @@ -61,10 +62,10 @@ const narrow = ref(false); const view = ref(null); const el = ref<HTMLDivElement | null>(null); const pageProps = ref({}); -const noMaintainerInformation = computed(() => isEmpty(instance.maintainerName) || isEmpty(instance.maintainerEmail)); -const noBotProtection = computed(() => !instance.disableRegistration && !instance.enableHcaptcha && !instance.enableRecaptcha && !instance.enableTurnstile && !instance.enableMcaptcha); -const noEmailServer = computed(() => !instance.enableEmail); -const noInquiryUrl = computed(() => isEmpty(instance.inquiryUrl)); +const noMaintainerInformation = computed(() => isEmpty(serverMetadata.maintainerName) || isEmpty(serverMetadata.maintainerEmail)); +const noBotProtection = computed(() => !serverMetadata.disableRegistration && !serverMetadata.enableHcaptcha && !serverMetadata.enableRecaptcha && !serverMetadata.enableTurnstile && !serverMetadata.enableMcaptcha); +const noEmailServer = computed(() => !serverMetadata.enableEmail); +const noInquiryUrl = computed(() => isEmpty(serverMetadata.inquiryUrl)); const thereIsUnresolvedAbuseReport = ref(false); const currentPage = computed(() => router.currentRef.value.child); @@ -88,7 +89,7 @@ const menuDef = computed(() => [{ icon: 'ti ti-search', text: i18n.ts.lookup, action: adminLookup, - }, ...(instance.disableRegistration ? [{ + }, ...(serverMetadata.disableRegistration ? [{ type: 'button', icon: 'ti ti-user-plus', text: i18n.ts.createInviteCode, diff --git a/packages/frontend/src/pages/admin/instance-block.vue b/packages/frontend/src/pages/admin/instance-block.vue index e090616b26..3fb8a7dc4c 100644 --- a/packages/frontend/src/pages/admin/instance-block.vue +++ b/packages/frontend/src/pages/admin/instance-block.vue @@ -38,7 +38,7 @@ import MkTextarea from '@/components/MkTextarea.vue'; import FormSuspense from '@/components/form/suspense.vue'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; -import { fetchInstance } from '@/instance.js'; +import { fetchServerMetadata } from '@/server-metadata.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; @@ -61,7 +61,7 @@ function save() { mediaSilencedHosts: mediaSilencedHosts.value.split('\n') || [], }).then(() => { - fetchInstance(true); + fetchServerMetadata(true); }); } diff --git a/packages/frontend/src/pages/admin/moderation.vue b/packages/frontend/src/pages/admin/moderation.vue index a75799696d..1702de2a9e 100644 --- a/packages/frontend/src/pages/admin/moderation.vue +++ b/packages/frontend/src/pages/admin/moderation.vue @@ -78,7 +78,7 @@ import MkTextarea from '@/components/MkTextarea.vue'; import FormSuspense from '@/components/form/suspense.vue'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; -import { fetchInstance } from '@/instance.js'; +import { fetchServerMetadata } from '@/server-metadata.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import MkButton from '@/components/MkButton.vue'; @@ -119,7 +119,7 @@ function save() { hiddenTags: hiddenTags.value.split('\n'), preservedUsernames: preservedUsernames.value.split('\n'), }).then(() => { - fetchInstance(true); + fetchServerMetadata(true); }); } diff --git a/packages/frontend/src/pages/admin/object-storage.vue b/packages/frontend/src/pages/admin/object-storage.vue index 5fddb715cd..931ca449cd 100644 --- a/packages/frontend/src/pages/admin/object-storage.vue +++ b/packages/frontend/src/pages/admin/object-storage.vue @@ -91,7 +91,7 @@ import FormSuspense from '@/components/form/suspense.vue'; import FormSplit from '@/components/form/split.vue'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; -import { fetchInstance } from '@/instance.js'; +import { fetchServerMetadata } from '@/server-metadata.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import MkButton from '@/components/MkButton.vue'; @@ -143,7 +143,7 @@ function save() { objectStorageSetPublicRead: objectStorageSetPublicRead.value, objectStorageS3ForcePathStyle: objectStorageS3ForcePathStyle.value, }).then(() => { - fetchInstance(true); + fetchServerMetadata(true); }); } diff --git a/packages/frontend/src/pages/admin/other-settings.vue b/packages/frontend/src/pages/admin/other-settings.vue index 345cf333b5..807bb20c71 100644 --- a/packages/frontend/src/pages/admin/other-settings.vue +++ b/packages/frontend/src/pages/admin/other-settings.vue @@ -48,7 +48,7 @@ import XHeader from './_header_.vue'; import FormSuspense from '@/components/form/suspense.vue'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; -import { fetchInstance } from '@/instance.js'; +import { fetchServerMetadata } from '@/server-metadata.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import MkSwitch from '@/components/MkSwitch.vue'; @@ -73,7 +73,7 @@ function save() { enableChartsForRemoteUser: enableChartsForRemoteUser.value, enableChartsForFederatedInstances: enableChartsForFederatedInstances.value, }).then(() => { - fetchInstance(true); + fetchServerMetadata(true); }); } diff --git a/packages/frontend/src/pages/admin/proxy-account.vue b/packages/frontend/src/pages/admin/proxy-account.vue index 81db9f1da9..aaf86ac4a7 100644 --- a/packages/frontend/src/pages/admin/proxy-account.vue +++ b/packages/frontend/src/pages/admin/proxy-account.vue @@ -29,7 +29,7 @@ import MkInfo from '@/components/MkInfo.vue'; import FormSuspense from '@/components/form/suspense.vue'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; -import { fetchInstance } from '@/instance.js'; +import { fetchServerMetadata } from '@/server-metadata.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; @@ -56,7 +56,7 @@ function save() { os.apiWithDialog('admin/update-meta', { proxyAccountId: proxyAccountId.value, }).then(() => { - fetchInstance(true); + fetchServerMetadata(true); }); } diff --git a/packages/frontend/src/pages/admin/roles.editor.vue b/packages/frontend/src/pages/admin/roles.editor.vue index 3e948abdf1..6fe42f0611 100644 --- a/packages/frontend/src/pages/admin/roles.editor.vue +++ b/packages/frontend/src/pages/admin/roles.editor.vue @@ -596,7 +596,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { watch, ref, computed } from 'vue'; +import { watch, ref, computed, inject } from 'vue'; import { throttle } from 'throttle-debounce'; import RolesEditorFormula from './RolesEditorFormula.vue'; import MkInput from '@/components/MkInput.vue'; @@ -609,9 +609,10 @@ import MkRange from '@/components/MkRange.vue'; import FormSlot from '@/components/form/slot.vue'; import { i18n } from '@/i18n.js'; import { ROLE_POLICIES } from '@/const.js'; -import { instance } from '@/instance.js'; import { deepClone } from '@/scripts/clone.js'; +const serverMetadata = inject('serverMetadata'); + const emit = defineEmits<{ (ev: 'update:modelValue', v: any): void; }>(); @@ -629,7 +630,7 @@ for (const ROLE_POLICY of ROLE_POLICIES) { role.value.policies[ROLE_POLICY] = { useDefault: true, priority: 0, - value: instance.policies[ROLE_POLICY], + value: serverMetadata.policies[ROLE_POLICY], }; } } diff --git a/packages/frontend/src/pages/admin/roles.role.vue b/packages/frontend/src/pages/admin/roles.role.vue index 8b3c906d8a..8dd9bcec8f 100644 --- a/packages/frontend/src/pages/admin/roles.role.vue +++ b/packages/frontend/src/pages/admin/roles.role.vue @@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkPagination :pagination="usersPagination"> <template #empty> <div class="_fullinfo"> - <img :src="infoImageUrl" class="_ghost"/> + <img v-if="serverMetadata.infoImageUrl" :src="serverMetadata.infoImageUrl" class="_ghost"/> <div>{{ i18n.ts.noUsers }}</div> </div> </template> @@ -74,7 +74,8 @@ import MkButton from '@/components/MkButton.vue'; import MkUserCardMini from '@/components/MkUserCardMini.vue'; import MkInfo from '@/components/MkInfo.vue'; import MkPagination from '@/components/MkPagination.vue'; -import { infoImageUrl } from '@/instance.js'; +import { inject } from 'vue'; +const serverMetadata = inject('serverMetadata'); import { useRouter } from '@/router/supplier.js'; const router = useRouter(); diff --git a/packages/frontend/src/pages/admin/roles.vue b/packages/frontend/src/pages/admin/roles.vue index 6fb950494b..e48cabe97b 100644 --- a/packages/frontend/src/pages/admin/roles.vue +++ b/packages/frontend/src/pages/admin/roles.vue @@ -251,7 +251,7 @@ import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; -import { instance, fetchInstance } from '@/instance.js'; +import { instance, fetchServerMetadata } from '@/server-metadata.js'; import MkFoldableSection from '@/components/MkFoldableSection.vue'; import { ROLE_POLICIES } from '@/const.js'; import { useRouter } from '@/router/supplier.js'; @@ -275,7 +275,7 @@ async function updateBaseRole() { await os.apiWithDialog('admin/roles/update-default-policies', { policies, }); - fetchInstance(true); + fetchServerMetadata(true); } function create() { diff --git a/packages/frontend/src/pages/admin/security.vue b/packages/frontend/src/pages/admin/security.vue index 9bccee89a5..b06ff05b3a 100644 --- a/packages/frontend/src/pages/admin/security.vue +++ b/packages/frontend/src/pages/admin/security.vue @@ -138,7 +138,7 @@ import MkButton from '@/components/MkButton.vue'; import MkTextarea from '@/components/MkTextarea.vue'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; -import { fetchInstance } from '@/instance.js'; +import { fetchServerMetadata } from '@/server-metadata.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; @@ -205,7 +205,7 @@ function save() { truemailAuthKey: truemailAuthKey.value, bannedEmailDomains: bannedEmailDomains.value.split('\n'), }).then(() => { - fetchInstance(true); + fetchServerMetadata(true); }); } diff --git a/packages/frontend/src/pages/admin/server-rules.vue b/packages/frontend/src/pages/admin/server-rules.vue index ff9b8d6299..e92b811122 100644 --- a/packages/frontend/src/pages/admin/server-rules.vue +++ b/packages/frontend/src/pages/admin/server-rules.vue @@ -41,24 +41,26 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { defineAsyncComponent, ref, computed } from 'vue'; +import { defineAsyncComponent, ref, computed, inject } from 'vue'; import XHeader from './_header_.vue'; import * as os from '@/os.js'; -import { fetchInstance, instance } from '@/instance.js'; +import { fetchServerMetadata } from '@/server-metadata.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import MkButton from '@/components/MkButton.vue'; import MkInput from '@/components/MkInput.vue'; +const serverMetadata = inject('serverMetadata'); + const Sortable = defineAsyncComponent(() => import('vuedraggable').then(x => x.default)); -const serverRules = ref<string[]>(instance.serverRules); +const serverRules = ref<string[]>(serverMetadata.serverRules); const save = async () => { await os.apiWithDialog('admin/update-meta', { serverRules: serverRules.value, }); - fetchInstance(true); + fetchServerMetadata(true); }; const remove = (index: number): void => { diff --git a/packages/frontend/src/pages/admin/settings.vue b/packages/frontend/src/pages/admin/settings.vue index 6f45c212ec..cbff964bb4 100644 --- a/packages/frontend/src/pages/admin/settings.vue +++ b/packages/frontend/src/pages/admin/settings.vue @@ -40,7 +40,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template #caption>{{ i18n.ts.repositoryUrlDescription }}</template> </MkInput> - <MkInfo v-if="!instance.providesTarball && !repositoryUrl" warn> + <MkInfo v-if="!serverMetadata.providesTarball && !repositoryUrl" warn> {{ i18n.ts.repositoryUrlOrTarballRequired }} </MkInfo> @@ -205,7 +205,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { ref, computed } from 'vue'; +import { ref, computed, inject } from 'vue'; import XHeader from './_header_.vue'; import MkSwitch from '@/components/MkSwitch.vue'; import MkInput from '@/components/MkInput.vue'; @@ -216,13 +216,15 @@ import FormSplit from '@/components/form/split.vue'; import FormSuspense from '@/components/form/suspense.vue'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; -import { fetchInstance, instance } from '@/instance.js'; +import { fetchServerMetadata } from '@/server-metadata.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import MkButton from '@/components/MkButton.vue'; import MkFolder from '@/components/MkFolder.vue'; import MkSelect from '@/components/MkSelect.vue'; +const serverMetadata = inject('serverMetadata'); + const name = ref<string | null>(null); const shortName = ref<string | null>(null); const description = ref<string | null>(null); @@ -310,7 +312,7 @@ async function save() { urlPreviewSummaryProxyUrl: urlPreviewSummaryProxyUrl.value, }); - fetchInstance(true); + fetchServerMetadata(true); } const headerTabs = computed(() => []); diff --git a/packages/frontend/src/pages/ads.vue b/packages/frontend/src/pages/ads.vue index b31807f9f5..8c46ca57b7 100644 --- a/packages/frontend/src/pages/ads.vue +++ b/packages/frontend/src/pages/ads.vue @@ -9,16 +9,18 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSpacer :contentMax="500"> <div class="_gaps"> - <MkAd v-for="ad in instance.ads" :key="ad.id" :specify="ad"/> + <MkAd v-for="ad in serverMetadata.ads" :key="ad.id" :specify="ad"/> </div> </MkSpacer> </MkStickyContainer> </template> <script lang="ts" setup> +import { inject } from 'vue'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { i18n } from '@/i18n.js'; -import { instance } from '@/instance.js'; + +const serverMetadata = inject('serverMetadata'); definePageMetadata(() => ({ title: i18n.ts.ads, diff --git a/packages/frontend/src/pages/contact.vue b/packages/frontend/src/pages/contact.vue index 1f2bee5a77..64d6f2fb6b 100644 --- a/packages/frontend/src/pages/contact.vue +++ b/packages/frontend/src/pages/contact.vue @@ -8,24 +8,24 @@ SPDX-License-Identifier: AGPL-3.0-only <template #header><MkPageHeader/></template> <MkSpacer :contentMax="600" :marginMin="20"> <div class="_gaps_m"> - <MkKeyValue :copy="instance.maintainerName"> + <MkKeyValue :copy="serverMetadata.maintainerName"> <template #key>{{ i18n.ts.administrator }}</template> <template #value> - <template v-if="instance.maintainerName">{{ instance.maintainerName }}</template> + <template v-if="serverMetadata.maintainerName">{{ serverMetadata.maintainerName }}</template> <span v-else style="opacity: 0.7;">({{ i18n.ts.none }})</span> </template> </MkKeyValue> - <MkKeyValue :copy="instance.maintainerEmail"> + <MkKeyValue :copy="serverMetadata.maintainerEmail"> <template #key>{{ i18n.ts.contact }}</template> <template #value> - <template v-if="instance.maintainerEmail">{{ instance.maintainerEmail }}</template> + <template v-if="serverMetadata.maintainerEmail">{{ serverMetadata.maintainerEmail }}</template> <span v-else style="opacity: 0.7;">({{ i18n.ts.none }})</span> </template> </MkKeyValue> - <MkKeyValue :copy="instance.inquiryUrl"> + <MkKeyValue :copy="serverMetadata.inquiryUrl"> <template #key>{{ i18n.ts.inquiry }}</template> <template #value> - <MkLink v-if="instance.inquiryUrl" :url="instance.inquiryUrl" target="_blank">{{ instance.inquiryUrl }}</MkLink> + <MkLink v-if="serverMetadata.inquiryUrl" :url="serverMetadata.inquiryUrl" target="_blank">{{ serverMetadata.inquiryUrl }}</MkLink> <span v-else style="opacity: 0.7;">({{ i18n.ts.none }})</span> </template> </MkKeyValue> @@ -35,12 +35,14 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> +import { inject } from 'vue'; import { i18n } from '@/i18n.js'; -import { instance } from '@/instance.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import MkKeyValue from '@/components/MkKeyValue.vue'; import MkLink from '@/components/MkLink.vue'; +const serverMetadata = inject('serverMetadata'); + definePageMetadata(() => ({ title: i18n.ts.inquiry, icon: 'ti ti-help-circle', diff --git a/packages/frontend/src/pages/drive.file.info.vue b/packages/frontend/src/pages/drive.file.info.vue index 3026d00a2c..579895e2bb 100644 --- a/packages/frontend/src/pages/drive.file.info.vue +++ b/packages/frontend/src/pages/drive.file.info.vue @@ -69,7 +69,7 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </div> <div v-else class="_fullinfo"> - <img :src="infoImageUrl" class="_ghost"/> + <img v-if="serverMetadata.infoImageUrl" :src="serverMetadata.infoImageUrl" class="_ghost"/> <div>{{ i18n.ts.nothing }}</div> </div> </div> @@ -82,7 +82,8 @@ import MkInfo from '@/components/MkInfo.vue'; import MkMediaList from '@/components/MkMediaList.vue'; import MkKeyValue from '@/components/MkKeyValue.vue'; import bytes from '@/filters/bytes.js'; -import { infoImageUrl } from '@/instance.js'; +import { inject } from 'vue'; +const serverMetadata = inject('serverMetadata'); import { i18n } from '@/i18n.js'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; @@ -99,12 +100,12 @@ const file = ref<Misskey.entities.DriveFile>(); const folderHierarchy = computed(() => { if (!file.value) return [i18n.ts.drive]; const folderNames = [i18n.ts.drive]; - + function get(folder: Misskey.entities.DriveFolder) { if (folder.parent) get(folder.parent); folderNames.push(folder.name); } - + if (file.value.folder) get(file.value.folder); return folderNames; }); diff --git a/packages/frontend/src/pages/favorites.vue b/packages/frontend/src/pages/favorites.vue index c3d4cae4aa..1bfbe6a5e9 100644 --- a/packages/frontend/src/pages/favorites.vue +++ b/packages/frontend/src/pages/favorites.vue @@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkPagination :pagination="pagination"> <template #empty> <div class="_fullinfo"> - <img :src="infoImageUrl" class="_ghost"/> + <img v-if="serverMetadata.infoImageUrl" :src="serverMetadata.infoImageUrl" class="_ghost"/> <div>{{ i18n.ts.noNotes }}</div> </div> </template> @@ -31,7 +31,8 @@ import MkNote from '@/components/MkNote.vue'; import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; -import { infoImageUrl } from '@/instance.js'; +import { inject } from 'vue'; +const serverMetadata = inject('serverMetadata'); const pagination = { endpoint: 'i/favorites' as const, diff --git a/packages/frontend/src/pages/follow-requests.vue b/packages/frontend/src/pages/follow-requests.vue index 8991af8086..9a7cc27bb2 100644 --- a/packages/frontend/src/pages/follow-requests.vue +++ b/packages/frontend/src/pages/follow-requests.vue @@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkPagination ref="paginationComponent" :pagination="pagination"> <template #empty> <div class="_fullinfo"> - <img :src="infoImageUrl" class="_ghost"/> + <img v-if="serverMetadata.infoImageUrl" :src="serverMetadata.infoImageUrl" class="_ghost"/> <div>{{ i18n.ts.noFollowRequests }}</div> </div> </template> @@ -44,7 +44,8 @@ import { userPage, acct } from '@/filters/user.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; -import { infoImageUrl } from '@/instance.js'; +import { inject } from 'vue'; +const serverMetadata = inject('serverMetadata'); const paginationComponent = shallowRef<InstanceType<typeof MkPagination>>(); diff --git a/packages/frontend/src/pages/instance-info.vue b/packages/frontend/src/pages/instance-info.vue index 4ba428d536..7fbb1b66c0 100644 --- a/packages/frontend/src/pages/instance-info.vue +++ b/packages/frontend/src/pages/instance-info.vue @@ -132,7 +132,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { ref, computed, watch } from 'vue'; +import { ref, computed, watch, inject } from 'vue'; import * as Misskey from 'misskey-js'; import MkChart from '@/components/MkChart.vue'; import MkObjectView from '@/components/MkObjectView.vue'; @@ -152,10 +152,11 @@ import { i18n } from '@/i18n.js'; import MkUserCardMini from '@/components/MkUserCardMini.vue'; import MkPagination from '@/components/MkPagination.vue'; import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; -import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js'; import { dateString } from '@/filters/date.js'; import MkTextarea from '@/components/MkTextarea.vue'; +const mediaProxy = inject('mediaProxy'); + const props = defineProps<{ host: string; }>(); @@ -198,7 +199,7 @@ async function fetch(): Promise<void> { isBlocked.value = instance.value?.isBlocked ?? false; isSilenced.value = instance.value?.isSilenced ?? false; isMediaSilenced.value = instance.value?.isMediaSilenced ?? false; - faviconUrl.value = getProxiedImageUrlNullable(instance.value?.faviconUrl, 'preview') ?? getProxiedImageUrlNullable(instance.value?.iconUrl, 'preview'); + faviconUrl.value = mediaProxy.getProxiedImageUrlNullable(instance.value?.faviconUrl, 'preview') ?? mediaProxy.getProxiedImageUrlNullable(instance.value?.iconUrl, 'preview'); moderationNote.value = instance.value?.moderationNote ?? ''; } diff --git a/packages/frontend/src/pages/invite.vue b/packages/frontend/src/pages/invite.vue index 25e56d2b8d..d9672be1a1 100644 --- a/packages/frontend/src/pages/invite.vue +++ b/packages/frontend/src/pages/invite.vue @@ -8,9 +8,9 @@ SPDX-License-Identifier: AGPL-3.0-only <template #header> <MkPageHeader/> </template> - <MKSpacer v-if="!instance.disableRegistration || !($i && ($i.isAdmin || $i.policies.canInvite))" :contentMax="1200"> + <MKSpacer v-if="!serverMetadata.disableRegistration || !($i && ($i.isAdmin || $i.policies.canInvite))" :contentMax="1200"> <div :class="$style.root"> - <img :class="$style.img" :src="serverErrorImageUrl" class="_ghost"/> + <img v-if="serverMetadata.serverErrorImageUrl" :class="$style.img" :src="serverMetadata.serverErrorImageUrl" class="_ghost"/> <div :class="$style.text"> <i class="ti ti-alert-triangle"></i> {{ i18n.ts.nothing }} @@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { computed, ref, shallowRef } from 'vue'; +import { computed, inject, ref, shallowRef } from 'vue'; import type * as Misskey from 'misskey-js'; import { i18n } from '@/i18n.js'; import * as os from '@/os.js'; @@ -45,13 +45,14 @@ import MkButton from '@/components/MkButton.vue'; import MkPagination, { Paging } from '@/components/MkPagination.vue'; import MkInviteCode from '@/components/MkInviteCode.vue'; import { definePageMetadata } from '@/scripts/page-metadata.js'; -import { serverErrorImageUrl, instance } from '@/instance.js'; import { $i } from '@/account.js'; +const serverMetadata = inject('serverMetadata'); + const pagingComponent = shallowRef<InstanceType<typeof MkPagination>>(); const currentInviteLimit = ref<null | number>(null); -const inviteLimit = (($i != null && $i.policies.inviteLimit) || (($i == null && instance.policies.inviteLimit))) as number; -const inviteLimitCycle = (($i != null && $i.policies.inviteLimitCycle) || ($i == null && instance.policies.inviteLimitCycle)) as number; +const inviteLimit = (($i != null && $i.policies.inviteLimit) || (($i == null && serverMetadata.policies.inviteLimit))) as number; +const inviteLimitCycle = (($i != null && $i.policies.inviteLimitCycle) || ($i == null && serverMetadata.policies.inviteLimitCycle)) as number; const pagination: Paging = { endpoint: 'invite/list' as const, diff --git a/packages/frontend/src/pages/list.vue b/packages/frontend/src/pages/list.vue index 954246ff93..17c178e26e 100644 --- a/packages/frontend/src/pages/list.vue +++ b/packages/frontend/src/pages/list.vue @@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template> <MKSpacer v-if="!(typeof error === 'undefined')" :contentMax="1200"> <div :class="$style.root"> - <img :class="$style.img" :src="serverErrorImageUrl" class="_ghost"/> + <img v-if="serverMetadata.serverErrorImageUrl" :class="$style.img" :src="serverMetadata.serverErrorImageUrl" class="_ghost"/> <p :class="$style.text"> <i class="ti ti-alert-triangle"></i> {{ i18n.ts.nothing }} @@ -34,7 +34,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { watch, computed, ref } from 'vue'; +import { watch, computed, ref, inject } from 'vue'; import * as Misskey from 'misskey-js'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; @@ -43,7 +43,8 @@ import { i18n } from '@/i18n.js'; import MkUserCardMini from '@/components/MkUserCardMini.vue'; import MkButton from '@/components/MkButton.vue'; import { definePageMetadata } from '@/scripts/page-metadata.js'; -import { serverErrorImageUrl } from '@/instance.js'; + +const serverMetadata = inject('serverMetadata'); const props = defineProps<{ listId: string; diff --git a/packages/frontend/src/pages/my-antennas/index.vue b/packages/frontend/src/pages/my-antennas/index.vue index 21c96348f0..134b369c84 100644 --- a/packages/frontend/src/pages/my-antennas/index.vue +++ b/packages/frontend/src/pages/my-antennas/index.vue @@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div> <div v-if="antennas.length === 0" class="empty"> <div class="_fullinfo"> - <img :src="infoImageUrl" class="_ghost"/> + <img v-if="serverMetadata.infoImageUrl" :src="serverMetadata.infoImageUrl" class="_ghost"/> <div>{{ i18n.ts.nothing }}</div> </div> </div> @@ -33,7 +33,8 @@ import MkButton from '@/components/MkButton.vue'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { antennasCache } from '@/cache.js'; -import { infoImageUrl } from '@/instance.js'; +import { inject } from 'vue'; +const serverMetadata = inject('serverMetadata'); const antennas = computed(() => antennasCache.value.value ?? []); diff --git a/packages/frontend/src/pages/my-lists/index.vue b/packages/frontend/src/pages/my-lists/index.vue index 82fde284c1..40456305e5 100644 --- a/packages/frontend/src/pages/my-lists/index.vue +++ b/packages/frontend/src/pages/my-lists/index.vue @@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div class="_gaps"> <div v-if="items.length === 0" class="empty"> <div class="_fullinfo"> - <img :src="infoImageUrl" class="_ghost"/> + <img v-if="serverMetadata.infoImageUrl" :src="serverMetadata.infoImageUrl" class="_ghost"/> <div>{{ i18n.ts.nothing }}</div> </div> </div> @@ -36,7 +36,8 @@ import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { userListsCache } from '@/cache.js'; -import { infoImageUrl } from '@/instance.js'; +import { inject } from 'vue'; +const serverMetadata = inject('serverMetadata'); import { signinRequired } from '@/account.js'; const $i = signinRequired(); diff --git a/packages/frontend/src/pages/not-found.vue b/packages/frontend/src/pages/not-found.vue index 93a792c42f..5497908fe5 100644 --- a/packages/frontend/src/pages/not-found.vue +++ b/packages/frontend/src/pages/not-found.vue @@ -6,18 +6,19 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div> <div class="_fullinfo"> - <img :src="notFoundImageUrl" class="_ghost"/> + <img v-if="serverMetadata.notFoundImageUrl" :src="serverMetadata.notFoundImageUrl" class="_ghost"/> <div>{{ i18n.ts.notFoundDescription }}</div> </div> </div> </template> <script lang="ts" setup> -import { computed } from 'vue'; +import { computed, inject } from 'vue'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import { pleaseLogin } from '@/scripts/please-login.js'; -import { notFoundImageUrl } from '@/instance.js'; + +const serverMetadata = inject('serverMetadata'); const props = defineProps<{ showLoginPopup?: boolean; diff --git a/packages/frontend/src/pages/page.vue b/packages/frontend/src/pages/page.vue index cb1ce9b918..90cf270e43 100644 --- a/packages/frontend/src/pages/page.vue +++ b/packages/frontend/src/pages/page.vue @@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only <img v-else-if="instance.backgroundImageUrl || instance.bannerUrl" :class="[$style.pageBannerBg, $style.pageBannerBgFallback1]" - :src="getStaticImageUrl(instance.backgroundImageUrl ?? instance.bannerUrl!)" + :src="mediaProxy.getStaticImageUrl(instance.backgroundImageUrl ?? instance.bannerUrl!)" /> <div v-else :class="[$style.pageBannerBg, $style.pageBannerBgFallback2]"></div> </div> @@ -98,7 +98,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { computed, watch, ref, defineAsyncComponent } from 'vue'; +import { computed, watch, ref, defineAsyncComponent, inject } from 'vue'; import * as Misskey from 'misskey-js'; import XPage from '@/components/page/page.vue'; import MkButton from '@/components/MkButton.vue'; @@ -117,12 +117,13 @@ import { pageViewInterruptors, defaultStore } from '@/store.js'; import { deepClone } from '@/scripts/clone.js'; import { $i } from '@/account.js'; import { isSupportShare } from '@/scripts/navigator.js'; -import { instance } from '@/instance.js'; -import { getStaticImageUrl } from '@/scripts/media-proxy.js'; import { copyToClipboard } from '@/scripts/copy-to-clipboard.js'; import { useRouter } from '@/router/supplier.js'; import { MenuItem } from '@/types/menu'; +const serverMetadata = inject('serverMetadata'); +const mediaProxy = inject('mediaProxy'); + const router = useRouter(); const props = defineProps<{ diff --git a/packages/frontend/src/pages/role.vue b/packages/frontend/src/pages/role.vue index ce80579cf9..62d7757d55 100644 --- a/packages/frontend/src/pages/role.vue +++ b/packages/frontend/src/pages/role.vue @@ -20,7 +20,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div v-if="role">{{ role.description }}</div> <MkUserList v-if="visible" :pagination="users" :extractor="(item) => item.user"/> <div v-else-if="!visible" class="_fullinfo"> - <img :src="infoImageUrl" class="_ghost"/> + <img v-if="serverMetadata.infoImageUrl" :src="serverMetadata.infoImageUrl" class="_ghost"/> <div>{{ i18n.ts.nothing }}</div> </div> </div> @@ -28,7 +28,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkSpacer v-else-if="tab === 'timeline'" :contentMax="700"> <MkTimeline v-if="visible" ref="timeline" src="role" :role="props.role"/> <div v-else-if="!visible" class="_fullinfo"> - <img :src="infoImageUrl" class="_ghost"/> + <img v-if="serverMetadata.infoImageUrl" :src="serverMetadata.infoImageUrl" class="_ghost"/> <div>{{ i18n.ts.nothing }}</div> </div> </MkSpacer> @@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { computed, watch, ref } from 'vue'; +import { computed, watch, ref, inject } from 'vue'; import * as Misskey from 'misskey-js'; import { misskeyApi } from '@/scripts/misskey-api.js'; import MkUserList from '@/components/MkUserList.vue'; @@ -44,7 +44,8 @@ import { definePageMetadata } from '@/scripts/page-metadata.js'; import { i18n } from '@/i18n.js'; import MkTimeline from '@/components/MkTimeline.vue'; import { instanceName } from '@/config.js'; -import { serverErrorImageUrl, infoImageUrl } from '@/instance.js'; + +const serverMetadata = inject('serverMetadata'); const props = withDefaults(defineProps<{ role: string; diff --git a/packages/frontend/src/pages/search.note.vue b/packages/frontend/src/pages/search.note.vue index 9cf7fbe8d8..79032cf7b4 100644 --- a/packages/frontend/src/pages/search.note.vue +++ b/packages/frontend/src/pages/search.note.vue @@ -51,7 +51,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { computed, ref, toRef, watch } from 'vue'; +import { computed, inject, ref, toRef, watch } from 'vue'; import type { UserDetailed } from 'misskey-js/entities.js'; import type { Paging } from '@/components/MkPagination.vue'; import MkNotes from '@/components/MkNotes.vue'; @@ -66,7 +66,8 @@ import { useRouter } from '@/router/supplier.js'; import MkUserCardMini from '@/components/MkUserCardMini.vue'; import MkRadios from '@/components/MkRadios.vue'; import { $i } from '@/account.js'; -import { instance } from '@/instance.js'; + +const serverMetadata = inject('serverMetadata'); const props = withDefaults(defineProps<{ query?: string; @@ -87,7 +88,7 @@ const notePagination = ref<Paging>(); const user = ref<UserDetailed | null>(null); const hostInput = ref(toRef(props, 'host').value); -const noteSearchableScope = instance.noteSearchableScope ?? 'local'; +const noteSearchableScope = serverMetadata.noteSearchableScope ?? 'local'; const hostSelect = ref<'all' | 'local' | 'specified'>('all'); @@ -121,7 +122,7 @@ if (props.userId != null) { } function selectUser() { - os.selectUser({ includeSelf: true, localOnly: instance.noteSearchableScope === 'local' }).then(_user => { + os.selectUser({ includeSelf: true, localOnly: serverMetadata.noteSearchableScope === 'local' }).then(_user => { user.value = _user; hostInput.value = _user.host ?? ''; }); diff --git a/packages/frontend/src/pages/search.vue b/packages/frontend/src/pages/search.vue index 38d7548fa8..ce34cc16d8 100644 --- a/packages/frontend/src/pages/search.vue +++ b/packages/frontend/src/pages/search.vue @@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkHorizontalSwipe v-model:tab="tab" :tabs="headerTabs"> <MkSpacer v-if="tab === 'note'" key="note" :contentMax="800"> - <div v-if="notesSearchAvailable || ignoreNotesSearchAvailable"> + <div v-if="isNotesSearchAvailable(serverMetadata) || ignoreNotesSearchAvailable"> <XNote v-bind="props"/> </div> <div v-else> @@ -25,13 +25,15 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { computed, defineAsyncComponent, ref, toRef } from 'vue'; +import { computed, defineAsyncComponent, inject, ref, toRef } from 'vue'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; -import { notesSearchAvailable } from '@/scripts/check-permissions.js'; +import { isNotesSearchAvailable } from '@/scripts/check-permissions.js'; import MkInfo from '@/components/MkInfo.vue'; import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; +const serverMetadata = inject('serverMetadata'); + const props = withDefaults(defineProps<{ query?: string, userId?: string, diff --git a/packages/frontend/src/pages/settings/apps.vue b/packages/frontend/src/pages/settings/apps.vue index 0e0c1f4c0c..47d02a56fb 100644 --- a/packages/frontend/src/pages/settings/apps.vue +++ b/packages/frontend/src/pages/settings/apps.vue @@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only <FormPagination ref="list" :pagination="pagination"> <template #empty> <div class="_fullinfo"> - <img :src="infoImageUrl" class="_ghost"/> + <img v-if="serverMetadata.infoImageUrl" :src="serverMetadata.infoImageUrl" class="_ghost"/> <div>{{ i18n.ts.nothing }}</div> </div> </template> @@ -52,7 +52,8 @@ import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; import MkKeyValue from '@/components/MkKeyValue.vue'; import MkButton from '@/components/MkButton.vue'; -import { infoImageUrl } from '@/instance.js'; +import { inject } from 'vue'; +const serverMetadata = inject('serverMetadata'); const list = ref<InstanceType<typeof FormPagination>>(); diff --git a/packages/frontend/src/pages/settings/email.vue b/packages/frontend/src/pages/settings/email.vue index f226647569..38badf05d7 100644 --- a/packages/frontend/src/pages/settings/email.vue +++ b/packages/frontend/src/pages/settings/email.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div v-if="instance.enableEmail" class="_gaps_m"> +<div v-if="serverMetadata.enableEmail" class="_gaps_m"> <FormSection first> <template #label>{{ i18n.ts.emailAddress }}</template> <MkInput v-model="emailAddress" type="email" manualSave> @@ -42,13 +42,13 @@ SPDX-License-Identifier: AGPL-3.0-only </div> </FormSection> </div> -<div v-if="!instance.enableEmail" class="_gaps_m"> +<div v-if="!serverMetadata.enableEmail" class="_gaps_m"> <MkInfo>{{ i18n.ts.emailNotSupported }}</MkInfo> </div> </template> <script lang="ts" setup> -import { onMounted, ref, watch, computed } from 'vue'; +import { onMounted, ref, watch, computed, inject } from 'vue'; import FormSection from '@/components/form/section.vue'; import MkInfo from '@/components/MkInfo.vue'; import MkInput from '@/components/MkInput.vue'; @@ -58,7 +58,8 @@ import { misskeyApi } from '@/scripts/misskey-api.js'; import { signinRequired } from '@/account.js'; import { i18n } from '@/i18n.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; -import { instance } from '@/instance.js'; + +const serverMetadata = inject('serverMetadata'); const $i = signinRequired(); diff --git a/packages/frontend/src/pages/settings/index.vue b/packages/frontend/src/pages/settings/index.vue index 7d16740a3e..5e2e7b03e0 100644 --- a/packages/frontend/src/pages/settings/index.vue +++ b/packages/frontend/src/pages/settings/index.vue @@ -28,17 +28,18 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script setup lang="ts"> -import { computed, onActivated, onMounted, onUnmounted, ref, shallowRef, watch } from 'vue'; +import { computed, inject, onActivated, onMounted, onUnmounted, ref, shallowRef, watch } from 'vue'; import { i18n } from '@/i18n.js'; import MkInfo from '@/components/MkInfo.vue'; import MkSuperMenu from '@/components/MkSuperMenu.vue'; import { signout, $i } from '@/account.js'; import { clearCache } from '@/scripts/clear-cache.js'; -import { instance } from '@/instance.js'; import { PageMetadata, definePageMetadata, provideMetadataReceiver, provideReactiveMetadata } from '@/scripts/page-metadata.js'; import * as os from '@/os.js'; import { useRouter } from '@/router/supplier.js'; +const serverMetadata = inject('serverMetadata'); + const indexInfo = { title: i18n.ts.settings, icon: 'ti ti-settings', diff --git a/packages/frontend/src/pages/settings/mute-block.vue b/packages/frontend/src/pages/settings/mute-block.vue index f4ee7dffbf..ac6d88cb44 100644 --- a/packages/frontend/src/pages/settings/mute-block.vue +++ b/packages/frontend/src/pages/settings/mute-block.vue @@ -33,7 +33,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkPagination :pagination="renoteMutingPagination"> <template #empty> <div class="_fullinfo"> - <img :src="infoImageUrl" class="_ghost"/> + <img v-if="serverMetadata.infoImageUrl" :src="serverMetadata.infoImageUrl" class="_ghost"/> <div>{{ i18n.ts.noUsers }}</div> </div> </template> @@ -64,7 +64,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkPagination :pagination="mutingPagination"> <template #empty> <div class="_fullinfo"> - <img :src="infoImageUrl" class="_ghost"/> + <img v-if="serverMetadata.infoImageUrl" :src="serverMetadata.infoImageUrl" class="_ghost"/> <div>{{ i18n.ts.noUsers }}</div> </div> </template> @@ -97,7 +97,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkPagination :pagination="blockingPagination"> <template #empty> <div class="_fullinfo"> - <img :src="infoImageUrl" class="_ghost"/> + <img v-if="serverMetadata.infoImageUrl" :src="serverMetadata.infoImageUrl" class="_ghost"/> <div>{{ i18n.ts.noUsers }}</div> </div> </template> @@ -136,7 +136,8 @@ import { definePageMetadata } from '@/scripts/page-metadata.js'; import MkUserCardMini from '@/components/MkUserCardMini.vue'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; -import { infoImageUrl } from '@/instance.js'; +import { inject } from 'vue'; +const serverMetadata = inject('serverMetadata'); import { signinRequired } from '@/account.js'; import MkFolder from '@/components/MkFolder.vue'; diff --git a/packages/frontend/src/pages/settings/theme.vue b/packages/frontend/src/pages/settings/theme.vue index 7d192bcbea..472be73133 100644 --- a/packages/frontend/src/pages/settings/theme.vue +++ b/packages/frontend/src/pages/settings/theme.vue @@ -71,7 +71,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { computed, onActivated, ref, watch } from 'vue'; +import { computed, inject, onActivated, ref, watch } from 'vue'; import JSON5 from 'json5'; import MkSwitch from '@/components/MkSwitch.vue'; import MkSelect from '@/components/MkSelect.vue'; @@ -83,7 +83,6 @@ import { selectFile } from '@/scripts/select-file.js'; import { isDeviceDarkmode } from '@/scripts/is-device-darkmode.js'; import { ColdDeviceStorage, defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; -import { instance } from '@/instance.js'; import { uniqueBy } from '@/scripts/array.js'; import { fetchThemes, getThemes } from '@/theme-store.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; @@ -91,6 +90,8 @@ import { miLocalStorage } from '@/local-storage.js'; import { unisonReload } from '@/scripts/unison-reload.js'; import * as os from '@/os.js'; +const serverMetadata = inject('serverMetadata'); + async function reloadAsk() { const { canceled } = await os.confirm({ type: 'info', @@ -104,10 +105,10 @@ async function reloadAsk() { const installedThemes = ref(getThemes()); const builtinThemes = getBuiltinThemesRef(); -const instanceDarkTheme = computed(() => instance.defaultDarkTheme ? JSON5.parse(instance.defaultDarkTheme) : null); +const instanceDarkTheme = computed(() => serverMetadata.defaultDarkTheme ? JSON5.parse(serverMetadata.defaultDarkTheme) : null); const installedDarkThemes = computed(() => installedThemes.value.filter(t => t.base === 'dark' || t.kind === 'dark')); const builtinDarkThemes = computed(() => builtinThemes.value.filter(t => t.base === 'dark' || t.kind === 'dark')); -const instanceLightTheme = computed(() => instance.defaultLightTheme ? JSON5.parse(instance.defaultLightTheme) : null); +const instanceLightTheme = computed(() => serverMetadata.defaultLightTheme ? JSON5.parse(serverMetadata.defaultLightTheme) : null); const installedLightThemes = computed(() => installedThemes.value.filter(t => t.base === 'light' || t.kind === 'light')); const builtinLightThemes = computed(() => builtinThemes.value.filter(t => t.base === 'light' || t.kind === 'light')); const themes = computed(() => uniqueBy([instanceDarkTheme.value, instanceLightTheme.value, ...builtinThemes.value, ...installedThemes.value].filter(x => x != null), theme => theme.id)); diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue index cc1ed3d01f..3a88264f8b 100644 --- a/packages/frontend/src/pages/timeline.vue +++ b/packages/frontend/src/pages/timeline.vue @@ -34,13 +34,14 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { computed, watch, provide, shallowRef, ref, onMounted, onActivated } from 'vue'; +import { computed, watch, provide, shallowRef, ref, onMounted, onActivated, inject } from 'vue'; +import { scroll } from '@@/js/scroll.js'; import type { Tab } from '@/components/global/MkPageHeader.tabs.vue'; +import type { BasicTimelineType } from '@/timelines.js'; import MkTimeline from '@/components/MkTimeline.vue'; import MkInfo from '@/components/MkInfo.vue'; import MkPostForm from '@/components/MkPostForm.vue'; import MkHorizontalSwipe from '@/components/MkHorizontalSwipe.vue'; -import { scroll } from '@@/js/scroll.js'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; import { defaultStore } from '@/store.js'; @@ -53,17 +54,18 @@ import { deepMerge } from '@/scripts/merge.js'; import { MenuItem } from '@/types/menu.js'; import { miLocalStorage } from '@/local-storage.js'; import { availableBasicTimelines, hasWithReplies, isAvailableBasicTimeline, isBasicTimeline, basicTimelineIconClass } from '@/timelines.js'; -import type { BasicTimelineType } from '@/timelines.js'; provide('shouldOmitHeaderTitle', true); +const serverMetadata = inject('serverMetadata'); + const tlComponent = shallowRef<InstanceType<typeof MkTimeline>>(); const rootEl = shallowRef<HTMLElement>(); type TimelinePageSrc = BasicTimelineType | `list:${string}`; const queue = ref(0); -const srcWhenNotSignin = ref<'local' | 'global'>(isAvailableBasicTimeline('local') ? 'local' : 'global'); +const srcWhenNotSignin = ref<'local' | 'global'>(isAvailableBasicTimeline(serverMetadata, 'local') ? 'local' : 'global'); const src = computed<TimelinePageSrc>({ get: () => ($i ? defaultStore.reactiveState.tl.value.src : srcWhenNotSignin.value), set: (x) => saveSrc(x), @@ -240,8 +242,8 @@ function closeTutorial(): void { } function switchTlIfNeeded() { - if (isBasicTimeline(src.value) && !isAvailableBasicTimeline(src.value)) { - src.value = availableBasicTimelines()[0]; + if (isBasicTimeline(src.value) && !isAvailableBasicTimeline(serverMetadata, src.value)) { + src.value = availableBasicTimelines(serverMetadata)[0]; } } @@ -297,7 +299,7 @@ const headerTabs = computed(() => [...(defaultStore.reactiveState.pinnedUserList title: l.name, icon: 'ti ti-star', iconOnly: true, -}))), ...availableBasicTimelines().map(tl => ({ +}))), ...availableBasicTimelines(serverMetadata).map(tl => ({ key: tl, title: i18n.ts._timelines[tl], icon: basicTimelineIconClass(tl), @@ -319,7 +321,7 @@ const headerTabs = computed(() => [...(defaultStore.reactiveState.pinnedUserList onClick: chooseChannel, }] as Tab[]); -const headerTabsWhenNotLogin = computed(() => [...availableBasicTimelines().map(tl => ({ +const headerTabsWhenNotLogin = computed(() => [...availableBasicTimelines(serverMetadata).map(tl => ({ key: tl, title: i18n.ts._timelines[tl], icon: basicTimelineIconClass(tl), diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue index 8e0292c7fe..34137b1567 100644 --- a/packages/frontend/src/pages/user/home.vue +++ b/packages/frontend/src/pages/user/home.vue @@ -151,8 +151,9 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { defineAsyncComponent, computed, onMounted, onUnmounted, nextTick, watch, ref } from 'vue'; +import { defineAsyncComponent, computed, onMounted, onUnmounted, nextTick, watch, ref, inject } from 'vue'; import * as Misskey from 'misskey-js'; +import { getScrollPosition } from '@@/js/scroll.js'; import MkNote from '@/components/MkNote.vue'; import MkFollowButton from '@/components/MkFollowButton.vue'; import MkAccountMoved from '@/components/MkAccountMoved.vue'; @@ -161,7 +162,6 @@ import MkTextarea from '@/components/MkTextarea.vue'; import MkOmit from '@/components/MkOmit.vue'; import MkInfo from '@/components/MkInfo.vue'; import MkButton from '@/components/MkButton.vue'; -import { getScrollPosition } from '@@/js/scroll.js'; import { getUserMenu } from '@/scripts/get-user-menu.js'; import number from '@/filters/number.js'; import { userPage } from '@/filters/user.js'; @@ -174,7 +174,9 @@ import { confetti } from '@/scripts/confetti.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; import { isFollowingVisibleForMe, isFollowersVisibleForMe } from '@/scripts/isFfVisibleForMe.js'; import { useRouter } from '@/router/supplier.js'; -import { getStaticImageUrl } from '@/scripts/media-proxy.js'; + +const serverMetadata = inject('serverMetadata'); +const mediaProxy = inject('mediaProxy'); function calcAge(birthdate: string): number { const date = new Date(birthdate); @@ -224,7 +226,7 @@ const style = computed(() => { if (props.user.bannerUrl == null) return {}; if (defaultStore.state.disableShowingAnimatedImages) { return { - backgroundImage: `url(${ getStaticImageUrl(props.user.bannerUrl) })`, + backgroundImage: `url(${ mediaProxy.getStaticImageUrl(props.user.bannerUrl) })`, }; } else { return { @@ -238,7 +240,7 @@ const age = computed(() => { }); function menu(ev: MouseEvent) { - const { menu, cleanup } = getUserMenu(user.value, router); + const { menu, cleanup } = getUserMenu({ serverMetadata }, user.value, router); os.popupMenu(menu, ev.currentTarget ?? ev.target).finally(cleanup); } diff --git a/packages/frontend/src/pages/user/index.files.vue b/packages/frontend/src/pages/user/index.files.vue index ce4d113cad..2cd321e222 100644 --- a/packages/frontend/src/pages/user/index.files.vue +++ b/packages/frontend/src/pages/user/index.files.vue @@ -33,9 +33,8 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onMounted, ref } from 'vue'; +import { inject, onMounted, ref } from 'vue'; import * as Misskey from 'misskey-js'; -import { getStaticImageUrl } from '@/scripts/media-proxy.js'; import { notePage } from '@/filters/note.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; import MkContainer from '@/components/MkContainer.vue'; @@ -43,6 +42,8 @@ import ImgWithBlurhash from '@/components/MkImgWithBlurhash.vue'; import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; +const mediaProxy = inject('mediaProxy'); + const props = defineProps<{ user: Misskey.entities.UserDetailed; }>(); @@ -56,7 +57,7 @@ const showingFiles = ref<string[]>([]); function thumbnail(image: Misskey.entities.DriveFile): string { return defaultStore.state.disableShowingAnimatedImages - ? getStaticImageUrl(image.url) + ? mediaProxy.getStaticImageUrl(image.url) : image.thumbnailUrl; } diff --git a/packages/frontend/src/pages/welcome.entrance.a.vue b/packages/frontend/src/pages/welcome.entrance.a.vue index d6ba397f1b..a54043e353 100644 --- a/packages/frontend/src/pages/welcome.entrance.a.vue +++ b/packages/frontend/src/pages/welcome.entrance.a.vue @@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only --> <template> -<div v-if="meta" class="rsqzvsbo"> +<div class="rsqzvsbo"> <MkFeaturedPhotos class="bg"/> <XTimeline class="tl"/> <div class="shape1"></div> @@ -36,7 +36,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { ref } from 'vue'; +import { inject, ref } from 'vue'; import * as Misskey from 'misskey-js'; import XTimeline from './welcome.timeline.vue'; import MarqueeText from '@/components/MkMarquee.vue'; @@ -44,8 +44,9 @@ import MkFeaturedPhotos from '@/components/MkFeaturedPhotos.vue'; import misskeysvg from '/client-assets/misskey.svg'; import { misskeyApiGet } from '@/scripts/misskey-api.js'; import MkVisitorDashboard from '@/components/MkVisitorDashboard.vue'; -import { getProxiedImageUrl } from '@/scripts/media-proxy.js'; -import { instance as meta } from '@/instance.js'; + +const serverMetadata = inject('serverMetadata'); +const mediaProxy = inject('mediaProxy'); const instances = ref<Misskey.entities.FederationInstance[]>(); @@ -53,7 +54,7 @@ function getInstanceIcon(instance: Misskey.entities.FederationInstance): string if (!instance.iconUrl) { return ''; } - return getProxiedImageUrl(instance.iconUrl, 'preview'); + return mediaProxy.getProxiedImageUrl(instance.iconUrl, 'preview'); } misskeyApiGet('federation/instances', { diff --git a/packages/frontend/src/pages/welcome.vue b/packages/frontend/src/pages/welcome.vue index 915fe35025..424b375f63 100644 --- a/packages/frontend/src/pages/welcome.vue +++ b/packages/frontend/src/pages/welcome.vue @@ -17,11 +17,11 @@ import XSetup from './welcome.setup.vue'; import XEntrance from './welcome.entrance.a.vue'; import { instanceName } from '@/config.js'; import { definePageMetadata } from '@/scripts/page-metadata.js'; -import { fetchInstance } from '@/instance.js'; +import { fetchServerMetadata } from '@/server-metadata.js'; const instance = ref<Misskey.entities.MetaDetailed | null>(null); -fetchInstance(true).then((res) => { +fetchServerMetadata(true).then((res) => { instance.value = res; }); diff --git a/packages/frontend/src/scripts/check-permissions.ts b/packages/frontend/src/scripts/check-permissions.ts index ed86529d5b..b5cfe50a02 100644 --- a/packages/frontend/src/scripts/check-permissions.ts +++ b/packages/frontend/src/scripts/check-permissions.ts @@ -3,17 +3,17 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { instance } from '@/instance.js'; +import * as Misskey from 'misskey-js'; import { $i } from '@/account.js'; -export const notesSearchAvailable = ( - // FIXME: instance.policies would be null in Vitest +export function isNotesSearchAvailable(serverMetadata: Misskey.entities.MetaDetailed): boolean { + // FIXME: serverMetadata.policies would be null in Vitest // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition - ($i == null && instance.policies != null && instance.policies.canSearchNotes) || + return ($i == null && serverMetadata.policies != null && serverMetadata.policies.canSearchNotes) || ($i != null && $i.policies.canSearchNotes) || - false -) as boolean; + false; +} -export const canSearchNonLocalNotes = ( - instance.noteSearchableScope === 'global' -); +export function canSearchNonLocalNotes(serverMetadata: Misskey.entities.MetaDetailed): boolean { + return serverMetadata.noteSearchableScope === 'global'; +} diff --git a/packages/frontend/src/scripts/clear-cache.ts b/packages/frontend/src/scripts/clear-cache.ts index 71d1232710..ec594cc316 100644 --- a/packages/frontend/src/scripts/clear-cache.ts +++ b/packages/frontend/src/scripts/clear-cache.ts @@ -7,7 +7,7 @@ import { unisonReload } from '@/scripts/unison-reload.js'; import * as os from '@/os.js'; import { miLocalStorage } from '@/local-storage.js'; import { fetchCustomEmojis } from '@/custom-emojis.js'; -import { fetchInstance } from '@/instance.js'; +import { fetchServerMetadata } from '@/server-metadata.js'; export async function clearCache() { os.waiting(); @@ -18,7 +18,7 @@ export async function clearCache() { miLocalStorage.removeItem('theme'); miLocalStorage.removeItem('emojis'); miLocalStorage.removeItem('lastEmojisFetchedAt'); - await fetchInstance(true); + await fetchServerMetadata(true); await fetchCustomEmojis(true); unisonReload(); } diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts index e0ccea813d..63e53d0b2e 100644 --- a/packages/frontend/src/scripts/get-note-menu.ts +++ b/packages/frontend/src/scripts/get-note-menu.ts @@ -8,7 +8,6 @@ import * as Misskey from 'misskey-js'; import { claimAchievement } from './achievements.js'; import { $i } from '@/account.js'; import { i18n } from '@/i18n.js'; -import { instance } from '@/instance.js'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; import { copyToClipboard } from '@/scripts/copy-to-clipboard.js'; @@ -170,7 +169,7 @@ function getNoteEmbedCodeMenu(note: Misskey.entities.Note, text: string): MenuIt }; } -export function getNoteMenu(props: { +export function getNoteMenu(ctx: { serverMetadata: Misskey.entities.MetaDetailed }, props: { note: Misskey.entities.Note; translation: Ref<Misskey.entities.NotesTranslateResponse | null>; translating: Ref<boolean>; @@ -330,7 +329,7 @@ export function getNoteMenu(props: { text: i18n.ts.share, action: share, }] : []), - $i && $i.policies.canUseTranslator && instance.translatorAvailable ? { + $i && $i.policies.canUseTranslator && ctx.serverMetadata.translatorAvailable ? { icon: 'ti ti-language-hiragana', text: i18n.ts.translate, action: translate, @@ -375,7 +374,7 @@ export function getNoteMenu(props: { text: i18n.ts.user, children: async () => { const user = appearNote.userId === $i?.id ? $i : await misskeyApi('users/show', { userId: appearNote.userId }); - const { menu, cleanup } = getUserMenu(user); + const { menu, cleanup } = getUserMenu(ctx, user); cleanups.push(cleanup); return menu; }, @@ -458,13 +457,13 @@ export function getNoteMenu(props: { text: i18n.ts.copyContent, action: copyContent, }, getCopyNoteLinkMenu(appearNote, i18n.ts.copyLink), - (appearNote.url || appearNote.uri) ? { - icon: 'ti ti-external-link', - text: i18n.ts.showOnRemote, - action: () => { - window.open(appearNote.url ?? appearNote.uri, '_blank', 'noopener'); - }, - } : getNoteEmbedCodeMenu(appearNote, i18n.ts.genEmbedCode)] + (appearNote.url || appearNote.uri) ? { + icon: 'ti ti-external-link', + text: i18n.ts.showOnRemote, + action: () => { + window.open(appearNote.url ?? appearNote.uri, '_blank', 'noopener'); + }, + } : getNoteEmbedCodeMenu(appearNote, i18n.ts.genEmbedCode)] .filter(x => x !== undefined); } diff --git a/packages/frontend/src/scripts/get-user-menu.ts b/packages/frontend/src/scripts/get-user-menu.ts index 035abc7bd0..5308ecd5ef 100644 --- a/packages/frontend/src/scripts/get-user-menu.ts +++ b/packages/frontend/src/scripts/get-user-menu.ts @@ -13,14 +13,14 @@ import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; import { defaultStore, userActions } from '@/store.js'; import { $i, iAmModerator } from '@/account.js'; -import { notesSearchAvailable, canSearchNonLocalNotes } from '@/scripts/check-permissions.js'; +import { isNotesSearchAvailable, canSearchNonLocalNotes } from '@/scripts/check-permissions.js'; import { IRouter } from '@/nirax.js'; import { antennasCache, rolesCache, userListsCache } from '@/cache.js'; import { mainRouter } from '@/router/main.js'; import { genEmbedCode } from '@/scripts/get-embed-code.js'; import { MenuItem } from '@/types/menu.js'; -export function getUserMenu(user: Misskey.entities.UserDetailed, router: IRouter = mainRouter) { +export function getUserMenu(ctx: { serverMetadata: Misskey.entities.MetaDetailed }, user: Misskey.entities.UserDetailed, router: IRouter = mainRouter) { const meId = $i ? $i.id : null; const cleanups = [] as (() => void)[]; @@ -154,7 +154,7 @@ export function getUserMenu(user: Misskey.entities.UserDetailed, router: IRouter action: () => { copyToClipboard(`@${user.username}@${user.host ?? host}`); }, - }, ...( notesSearchAvailable && (user.host == null || canSearchNonLocalNotes) ? [{ + }, ...(isNotesSearchAvailable(ctx.serverMetadata) && (user.host == null || canSearchNonLocalNotes(ctx.serverMetadata)) ? [{ icon: 'ti ti-search', text: i18n.ts.searchThisUsersNotes, action: () => { diff --git a/packages/frontend/src/scripts/media-proxy.ts b/packages/frontend/src/scripts/media-proxy.ts deleted file mode 100644 index facd50ac41..0000000000 --- a/packages/frontend/src/scripts/media-proxy.ts +++ /dev/null @@ -1,53 +0,0 @@ -/* - * SPDX-FileCopyrightText: syuilo and misskey-project - * SPDX-License-Identifier: AGPL-3.0-only - */ - -import { query } from '@@/js/url.js'; -import { url } from '@/config.js'; -import { instance } from '@/instance.js'; - -export function getProxiedImageUrl(imageUrl: string, type?: 'preview' | 'emoji' | 'avatar', mustOrigin = false, noFallback = false): string { - const localProxy = `${url}/proxy`; - - if (imageUrl.startsWith(instance.mediaProxy + '/') || imageUrl.startsWith('/proxy/') || imageUrl.startsWith(localProxy + '/')) { - // もう既にproxyっぽそうだったらurlを取り出す - imageUrl = (new URL(imageUrl)).searchParams.get('url') ?? imageUrl; - } - - return `${mustOrigin ? localProxy : instance.mediaProxy}/${ - type === 'preview' ? 'preview.webp' - : 'image.webp' - }?${query({ - url: imageUrl, - ...(!noFallback ? { 'fallback': '1' } : {}), - ...(type ? { [type]: '1' } : {}), - ...(mustOrigin ? { origin: '1' } : {}), - })}`; -} - -export function getProxiedImageUrlNullable(imageUrl: string | null | undefined, type?: 'preview'): string | null { - if (imageUrl == null) return null; - return getProxiedImageUrl(imageUrl, type); -} - -export function getStaticImageUrl(baseUrl: string): string { - const u = baseUrl.startsWith('http') ? new URL(baseUrl) : new URL(baseUrl, url); - - if (u.href.startsWith(`${url}/emoji/`)) { - // もう既にemojiっぽそうだったらsearchParams付けるだけ - u.searchParams.set('static', '1'); - return u.href; - } - - if (u.href.startsWith(instance.mediaProxy + '/')) { - // もう既にproxyっぽそうだったらsearchParams付けるだけ - u.searchParams.set('static', '1'); - return u.href; - } - - return `${instance.mediaProxy}/static.webp?${query({ - url: u.href, - static: '1', - })}`; -} diff --git a/packages/frontend/src/scripts/upload.ts b/packages/frontend/src/scripts/upload.ts index abb0e1e677..138bb4b044 100644 --- a/packages/frontend/src/scripts/upload.ts +++ b/packages/frontend/src/scripts/upload.ts @@ -13,7 +13,7 @@ import { apiUrl } from '@/config.js'; import { $i } from '@/account.js'; import { alert } from '@/os.js'; import { i18n } from '@/i18n.js'; -import { instance } from '@/instance.js'; +import { fetchServerMetadata } from '@/server-metadata.js'; type Uploading = { id: string; @@ -40,16 +40,16 @@ export function uploadFile( if (folder && typeof folder === 'object') folder = folder.id; - if (file.size > instance.maxFileSize) { - alert({ - type: 'error', - title: i18n.ts.failedToUpload, - text: i18n.ts.cannotUploadBecauseExceedsFileSizeLimit, - }); - return Promise.reject(); - } + return fetchServerMetadata().then((serverMetadata) => new Promise((resolve, reject) => { + if (file.size > serverMetadata.maxFileSize) { + alert({ + type: 'error', + title: i18n.ts.failedToUpload, + text: i18n.ts.cannotUploadBecauseExceedsFileSizeLimit, + }); + return reject(); + } - return new Promise((resolve, reject) => { const id = uuid(); const reader = new FileReader(); @@ -158,5 +158,5 @@ export function uploadFile( xhr.send(formData); }; reader.readAsArrayBuffer(file); - }); + })); } diff --git a/packages/frontend/src/instance.ts b/packages/frontend/src/server-metadata.ts similarity index 51% rename from packages/frontend/src/instance.ts rename to packages/frontend/src/server-metadata.ts index 6847321d6c..470abdd69c 100644 --- a/packages/frontend/src/instance.ts +++ b/packages/frontend/src/server-metadata.ts @@ -3,11 +3,9 @@ * SPDX-License-Identifier: AGPL-3.0-only */ -import { computed, reactive } from 'vue'; import * as Misskey from 'misskey-js'; import { misskeyApi } from '@/scripts/misskey-api.js'; import { miLocalStorage } from '@/local-storage.js'; -import { DEFAULT_INFO_IMAGE_URL, DEFAULT_NOT_FOUND_IMAGE_URL, DEFAULT_SERVER_ERROR_IMAGE_URL } from '@/const.js'; // TODO: 他のタブと永続化されたstateを同期 @@ -26,37 +24,23 @@ if (providedAt > cachedAt) { } //#endregion -// TODO: instanceをリアクティブにするかは再考の余地あり - -export const instance: Misskey.entities.MetaDetailed = reactive(cachedMeta ?? {}); - -export const serverErrorImageUrl = computed(() => instance.serverErrorImageUrl ?? DEFAULT_SERVER_ERROR_IMAGE_URL); - -export const infoImageUrl = computed(() => instance.infoImageUrl ?? DEFAULT_INFO_IMAGE_URL); - -export const notFoundImageUrl = computed(() => instance.notFoundImageUrl ?? DEFAULT_NOT_FOUND_IMAGE_URL); - -export const isEnabledUrlPreview = computed(() => instance.enableUrlPreview ?? true); - -export async function fetchInstance(force = false): Promise<Misskey.entities.MetaDetailed> { - if (!force) { - const cachedAt = miLocalStorage.getItem('instanceCachedAt') ? parseInt(miLocalStorage.getItem('instanceCachedAt')!) : 0; +let metadata: Misskey.entities.MetaDetailed | null = cachedMeta ?? null; +// TODO: 短時間に複数回呼ばれてもリクエストは一回だけにする +export async function fetchServerMetadata(force = false): Promise<Misskey.entities.MetaDetailed> { + if (!force && metadata != null) { if (Date.now() - cachedAt < 1000 * 60 * 60) { - return instance; + return metadata; } } - const meta = await misskeyApi('meta', { + metadata = await misskeyApi('meta', { detail: true, }); - for (const [k, v] of Object.entries(meta)) { - instance[k] = v; - } - - miLocalStorage.setItem('instance', JSON.stringify(instance)); + cachedAt = Date.now(); + miLocalStorage.setItem('instance', JSON.stringify(metadata)); miLocalStorage.setItem('instanceCachedAt', Date.now().toString()); - return instance; + return metadata!; } diff --git a/packages/frontend/src/timelines.ts b/packages/frontend/src/timelines.ts index 94eda3545e..1e155de472 100644 --- a/packages/frontend/src/timelines.ts +++ b/packages/frontend/src/timelines.ts @@ -3,8 +3,8 @@ * SPDX-License-Identifier: AGPL-3.0-only */ +import * as Misskey from 'misskey-js'; import { $i } from '@/account.js'; -import { instance } from '@/instance.js'; export const basicTimelineTypes = [ 'home', @@ -32,23 +32,23 @@ export function basicTimelineIconClass(timeline: BasicTimelineType): string { } } -export function isAvailableBasicTimeline(timeline: BasicTimelineType | undefined | null): boolean { +export function isAvailableBasicTimeline(metadata: Misskey.entities.MetaDetailed, timeline: BasicTimelineType | undefined | null): boolean { switch (timeline) { case 'home': return $i != null; case 'local': - return ($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable); + return ($i == null && metadata.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable); case 'social': return $i != null && $i.policies.ltlAvailable; case 'global': - return ($i == null && instance.policies.gtlAvailable) || ($i != null && $i.policies.gtlAvailable); + return ($i == null && metadata.policies.gtlAvailable) || ($i != null && $i.policies.gtlAvailable); default: return false; } } -export function availableBasicTimelines(): BasicTimelineType[] { - return basicTimelineTypes.filter(isAvailableBasicTimeline); +export function availableBasicTimelines(metadata: Misskey.entities.MetaDetailed): BasicTimelineType[] { + return basicTimelineTypes.filter(timeline => isAvailableBasicTimeline(metadata, timeline)); } export function hasWithReplies(timeline: BasicTimelineType | undefined | null): boolean { diff --git a/packages/frontend/src/ui/_common_/common.ts b/packages/frontend/src/ui/_common_/common.ts index 74c3028745..1104c5636b 100644 --- a/packages/frontend/src/ui/_common_/common.ts +++ b/packages/frontend/src/ui/_common_/common.ts @@ -6,10 +6,10 @@ import { defineAsyncComponent } from 'vue'; import type { MenuItem } from '@/types/menu.js'; import * as os from '@/os.js'; -import { instance } from '@/instance.js'; import { host } from '@/config.js'; import { i18n } from '@/i18n.js'; import { $i } from '@/account.js'; +import { fetchServerMetadata } from '@/server-metadata.js'; function toolsMenuItems(): MenuItem[] { return [{ @@ -40,9 +40,10 @@ function toolsMenuItems(): MenuItem[] { } : undefined]; } -export function openInstanceMenu(ev: MouseEvent) { +export async function openInstanceMenu(ev: MouseEvent) { + const serverMetadata = await fetchServerMetadata(); os.popupMenu([{ - text: instance.name ?? host, + text: serverMetadata.name ?? host, type: 'label', }, { type: 'link', @@ -69,7 +70,7 @@ export function openInstanceMenu(ev: MouseEvent) { text: i18n.ts.ads, icon: 'ti ti-ad', to: '/ads', - }, ($i && ($i.isAdmin || $i.policies.canInvite) && instance.disableRegistration) ? { + }, ($i && ($i.isAdmin || $i.policies.canInvite) && serverMetadata.disableRegistration) ? { type: 'link', to: '/invite', text: i18n.ts.invite, @@ -84,25 +85,25 @@ export function openInstanceMenu(ev: MouseEvent) { text: i18n.ts.inquiry, icon: 'ti ti-help-circle', to: '/contact', - }, (instance.impressumUrl) ? { + }, (serverMetadata.impressumUrl) ? { type: 'a', text: i18n.ts.impressum, icon: 'ti ti-file-invoice', - href: instance.impressumUrl, + href: serverMetadata.impressumUrl, target: '_blank', - } : undefined, (instance.tosUrl) ? { + } : undefined, (serverMetadata.tosUrl) ? { type: 'a', text: i18n.ts.termsOfService, icon: 'ti ti-notebook', - href: instance.tosUrl, + href: serverMetadata.tosUrl, target: '_blank', - } : undefined, (instance.privacyPolicyUrl) ? { + } : undefined, (serverMetadata.privacyPolicyUrl) ? { type: 'a', text: i18n.ts.privacyPolicy, icon: 'ti ti-shield-lock', - href: instance.privacyPolicyUrl, + href: serverMetadata.privacyPolicyUrl, target: '_blank', - } : undefined, (!instance.impressumUrl && !instance.tosUrl && !instance.privacyPolicyUrl) ? undefined : { type: 'divider' }, { + } : undefined, (!serverMetadata.impressumUrl && !serverMetadata.tosUrl && !serverMetadata.privacyPolicyUrl) ? undefined : { type: 'divider' }, { type: 'a', text: i18n.ts.document, icon: 'ti ti-bulb', diff --git a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue index e80d5fd399..96e7b504ae 100644 --- a/packages/frontend/src/ui/_common_/navbar-for-mobile.vue +++ b/packages/frontend/src/ui/_common_/navbar-for-mobile.vue @@ -6,9 +6,9 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div :class="$style.root"> <div :class="$style.top"> - <div :class="$style.banner" :style="{ backgroundImage: `url(${ instance.bannerUrl })` }"></div> + <div :class="$style.banner" :style="{ backgroundImage: `url(${ serverMetadata.bannerUrl })` }"></div> <button class="_button" :class="$style.instance" @click="openInstanceMenu"> - <img :src="instance.iconUrl || instance.faviconUrl || '/favicon.ico'" alt="" :class="$style.instanceIcon"/> + <img :src="serverMetadata.iconUrl || serverMetadata.faviconUrl || '/favicon.ico'" alt="" :class="$style.instanceIcon"/> </button> </div> <div :class="$style.middle"> @@ -49,14 +49,15 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { computed, defineAsyncComponent, toRef } from 'vue'; +import { computed, defineAsyncComponent, inject, toRef } from 'vue'; import { openInstanceMenu } from './common.js'; import * as os from '@/os.js'; import { navbarItemDef } from '@/navbar.js'; import { $i, openAccountMenu as openAccountMenu_ } from '@/account.js'; import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; -import { instance } from '@/instance.js'; + +const serverMetadata = inject('serverMetadata'); const menu = toRef(defaultStore.state, 'menu'); const otherMenuItemIndicated = computed(() => { diff --git a/packages/frontend/src/ui/_common_/navbar.vue b/packages/frontend/src/ui/_common_/navbar.vue index 2b116909ba..e71c4fefe4 100644 --- a/packages/frontend/src/ui/_common_/navbar.vue +++ b/packages/frontend/src/ui/_common_/navbar.vue @@ -7,9 +7,9 @@ SPDX-License-Identifier: AGPL-3.0-only <div :class="[$style.root, { [$style.iconOnly]: iconOnly }]"> <div :class="$style.body"> <div :class="$style.top"> - <div :class="$style.banner" :style="{ backgroundImage: `url(${ instance.bannerUrl })` }"></div> - <button v-tooltip.noDelay.right="instance.name ?? i18n.ts.instance" class="_button" :class="$style.instance" @click="openInstanceMenu"> - <img :src="instance.iconUrl || instance.faviconUrl || '/favicon.ico'" alt="" :class="$style.instanceIcon"/> + <div :class="$style.banner" :style="{ backgroundImage: `url(${ serverMetadata.bannerUrl })` }"></div> + <button v-tooltip.noDelay.right="serverMetadata.name ?? i18n.ts.instance" class="_button" :class="$style.instance" @click="openInstanceMenu"> + <img :src="serverMetadata.iconUrl || serverMetadata.faviconUrl || '/favicon.ico'" alt="" :class="$style.instanceIcon"/> </button> </div> <div :class="$style.middle"> @@ -60,14 +60,15 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { computed, defineAsyncComponent, ref, watch } from 'vue'; +import { computed, defineAsyncComponent, inject, ref, watch } from 'vue'; import { openInstanceMenu } from './common.js'; import * as os from '@/os.js'; import { navbarItemDef } from '@/navbar.js'; import { $i, openAccountMenu as openAccountMenu_ } from '@/account.js'; import { defaultStore } from '@/store.js'; import { i18n } from '@/i18n.js'; -import { instance } from '@/instance.js'; + +const serverMetadata = inject('serverMetadata'); const iconOnly = ref(false); diff --git a/packages/frontend/src/ui/_common_/statusbar-federation.vue b/packages/frontend/src/ui/_common_/statusbar-federation.vue index e234bb3a33..d5dc8f4123 100644 --- a/packages/frontend/src/ui/_common_/statusbar-federation.vue +++ b/packages/frontend/src/ui/_common_/statusbar-federation.vue @@ -31,12 +31,13 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { ref } from 'vue'; +import { inject, ref } from 'vue'; import * as Misskey from 'misskey-js'; +import { useInterval } from '@@/js/use-interval.js'; import MarqueeText from '@/components/MkMarquee.vue'; import { misskeyApi } from '@/scripts/misskey-api.js'; -import { useInterval } from '@@/js/use-interval.js'; -import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js'; + +const mediaProxy = inject('mediaProxy'); const props = defineProps<{ display?: 'marquee' | 'oneByOne'; @@ -68,7 +69,7 @@ useInterval(tick, Math.max(5000, props.refreshIntervalSec * 1000), { }); function getInstanceIcon(instance): string { - return getProxiedImageUrlNullable(instance.iconUrl, 'preview') ?? getProxiedImageUrlNullable(instance.faviconUrl, 'preview') ?? '/client-assets/dummy.png'; + return mediaProxy.getProxiedImageUrlNullable(instance.iconUrl, 'preview') ?? mediaProxy.getProxiedImageUrlNullable(instance.faviconUrl, 'preview') ?? '/client-assets/dummy.png'; } </script> diff --git a/packages/frontend/src/ui/classic.header.vue b/packages/frontend/src/ui/classic.header.vue index c03afd6cd6..157c848de8 100644 --- a/packages/frontend/src/ui/classic.header.vue +++ b/packages/frontend/src/ui/classic.header.vue @@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div class="body"> <div class="left"> <button v-click-anime class="item _button instance" @click="openInstanceMenu"> - <img :src="instance.iconUrl ?? instance.faviconUrl ?? '/favicon.ico'" class="_ghost"/> + <img :src="serverMetadata.iconUrl ?? serverMetadata.faviconUrl ?? '/favicon.ico'" class="_ghost"/> </button> <MkA v-click-anime v-tooltip="i18n.ts.timeline" class="item index" activeClass="active" to="/" exact> <i class="ti ti-home ti-fw"></i> @@ -47,16 +47,17 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { computed, defineAsyncComponent, onMounted, ref } from 'vue'; +import { computed, defineAsyncComponent, inject, onMounted, ref } from 'vue'; import { openInstanceMenu } from './_common_/common.js'; import * as os from '@/os.js'; import { navbarItemDef } from '@/navbar.js'; import { openAccountMenu as openAccountMenu_, $i } from '@/account.js'; import MkButton from '@/components/MkButton.vue'; import { defaultStore } from '@/store.js'; -import { instance } from '@/instance.js'; import { i18n } from '@/i18n.js'; +const serverMetadata = inject('serverMetadata'); + const WINDOW_THRESHOLD = 1400; const settingsWindowed = ref(window.innerWidth > WINDOW_THRESHOLD); diff --git a/packages/frontend/src/ui/classic.sidebar.vue b/packages/frontend/src/ui/classic.sidebar.vue index d8574a915f..fbcb78b397 100644 --- a/packages/frontend/src/ui/classic.sidebar.vue +++ b/packages/frontend/src/ui/classic.sidebar.vue @@ -41,7 +41,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div class="divider"></div> <div class="about"> <button v-click-anime class="item _button" @click="openInstanceMenu"> - <img :src="instance.iconUrl ?? instance.faviconUrl ?? '/favicon.ico'" class="_ghost"/> + <img :src="serverMetadata.iconUrl ?? serverMetadata.faviconUrl ?? '/favicon.ico'" class="_ghost"/> </button> </div> <!--<MisskeyLogo class="misskey"/>--> @@ -49,7 +49,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { defineAsyncComponent, computed, watch, ref, shallowRef } from 'vue'; +import { defineAsyncComponent, computed, watch, ref, shallowRef, inject } from 'vue'; import { openInstanceMenu } from './_common_/common.js'; // import { host } from '@/config.js'; import * as os from '@/os.js'; @@ -60,9 +60,10 @@ import MkButton from '@/components/MkButton.vue'; // import { mainRouter } from '@/router.js'; //import MisskeyLogo from '@assets/client/misskey.svg'; import { defaultStore } from '@/store.js'; -import { instance } from '@/instance.js'; import { i18n } from '@/i18n.js'; +const serverMetadata = inject('serverMetadata'); + const WINDOW_THRESHOLD = 1400; const menu = ref(defaultStore.state.menu); diff --git a/packages/frontend/src/ui/deck/tl-column.vue b/packages/frontend/src/ui/deck/tl-column.vue index e210ee7b7a..eef9abf849 100644 --- a/packages/frontend/src/ui/deck/tl-column.vue +++ b/packages/frontend/src/ui/deck/tl-column.vue @@ -10,7 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only <span style="margin-left: 8px;">{{ column.name }}</span> </template> - <div v-if="!isAvailableBasicTimeline(column.tl)" :class="$style.disabled"> + <div v-if="!isAvailableBasicTimeline(serverMetadata, column.tl)" :class="$style.disabled"> <p :class="$style.disabledTitle"> <i class="ti ti-circle-minus"></i> {{ i18n.ts._disabledTimeline.title }} @@ -31,7 +31,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onMounted, watch, ref, shallowRef, computed } from 'vue'; +import { onMounted, watch, ref, shallowRef, computed, inject } from 'vue'; import XColumn from './column.vue'; import { removeColumn, updateColumn, Column } from './deck-store.js'; import type { MenuItem } from '@/types/menu.js'; @@ -39,11 +39,12 @@ import MkTimeline from '@/components/MkTimeline.vue'; import * as os from '@/os.js'; import { i18n } from '@/i18n.js'; import { hasWithReplies, isAvailableBasicTimeline, basicTimelineIconClass } from '@/timelines.js'; -import { instance } from '@/instance.js'; import { SoundStore } from '@/store.js'; import { soundSettingsButton } from '@/ui/deck/tl-note-notification.js'; import * as sound from '@/scripts/sound.js'; +const serverMetadata = inject('serverMetadata'); + const props = defineProps<{ column: Column; isStacked: boolean; diff --git a/packages/frontend/src/ui/visitor.vue b/packages/frontend/src/ui/visitor.vue index c229946bd4..455961c7f6 100644 --- a/packages/frontend/src/ui/visitor.vue +++ b/packages/frontend/src/ui/visitor.vue @@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only <a v-if="isRoot" href="https://github.com/misskey-dev/misskey" target="_blank" class="github-corner" aria-label="View source on GitHub"><svg width="80" height="80" viewBox="0 0 250 250" style="fill:var(--panel); color:var(--fg); position: fixed; z-index: 10; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a> <div v-if="!narrow && !isRoot" class="side"> - <div class="banner" :style="{ backgroundImage: instance.backgroundImageUrl ? `url(${ instance.backgroundImageUrl })` : 'none' }"></div> + <div class="banner" :style="{ backgroundImage: serverMetadata.backgroundImageUrl ? `url(${ serverMetadata.backgroundImageUrl })` : 'none' }"></div> <div class="dashboard"> <MkVisitorDashboard/> </div> @@ -69,11 +69,10 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onMounted, provide, ref, computed } from 'vue'; +import { onMounted, provide, ref, computed, inject } from 'vue'; import XCommon from './_common_/common.vue'; import { instanceName } from '@/config.js'; import * as os from '@/os.js'; -import { instance } from '@/instance.js'; import XSigninDialog from '@/components/MkSigninDialog.vue'; import XSignupDialog from '@/components/MkSignupDialog.vue'; import { ColdDeviceStorage, defaultStore } from '@/store.js'; @@ -82,6 +81,8 @@ import { i18n } from '@/i18n.js'; import MkVisitorDashboard from '@/components/MkVisitorDashboard.vue'; import { mainRouter } from '@/router/main.js'; +const serverMetadata = inject('serverMetadata'); + const isRoot = computed(() => mainRouter.currentRoute.value.name === 'index'); const DESKTOP_THRESHOLD = 1100; @@ -107,7 +108,7 @@ const announcements = { limit: 10, }; -const isTimelineAvailable = ref(instance.policies?.ltlAvailable || instance.policies?.gtlAvailable); +const isTimelineAvailable = ref(serverMetadata.policies?.ltlAvailable || serverMetadata.policies?.gtlAvailable); const showMenu = ref(false); const isDesktop = ref(window.innerWidth >= DESKTOP_THRESHOLD); diff --git a/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue b/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue index bcfaaf00ab..34ef3b90dc 100644 --- a/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue +++ b/packages/frontend/src/widgets/WidgetBirthdayFollowings.vue @@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only <MkAvatar v-for="user in users" :key="user.id" :user="user.followee" link preview></MkAvatar> </div> <div v-else :class="$style.bdayFFallback"> - <img :src="infoImageUrl" class="_ghost" :class="$style.bdayFFallbackImage"/> + <img v-if="serverMetadata.infoImageUrl" :src="serverMetadata.infoImageUrl" class="_ghost" :class="$style.bdayFFallbackImage"/> <div>{{ i18n.ts.nothing }}</div> </div> </div> @@ -31,7 +31,8 @@ import { GetFormResultType } from '@/scripts/form.js'; import MkContainer from '@/components/MkContainer.vue'; import { misskeyApi } from '@/scripts/misskey-api.js'; import { i18n } from '@/i18n.js'; -import { infoImageUrl } from '@/instance.js'; +import { inject } from 'vue'; +const serverMetadata = inject('serverMetadata'); import { $i } from '@/account.js'; const name = i18n.ts._widgets.birthdayFollowings; diff --git a/packages/frontend/src/widgets/WidgetFederation.vue b/packages/frontend/src/widgets/WidgetFederation.vue index c10416e4fb..507f488a92 100644 --- a/packages/frontend/src/widgets/WidgetFederation.vue +++ b/packages/frontend/src/widgets/WidgetFederation.vue @@ -25,16 +25,15 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { ref } from 'vue'; +import { inject, ref } from 'vue'; import * as Misskey from 'misskey-js'; +import { useInterval } from '@@/js/use-interval.js'; import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import MkContainer from '@/components/MkContainer.vue'; import MkMiniChart from '@/components/MkMiniChart.vue'; import { misskeyApi, misskeyApiGet } from '@/scripts/misskey-api.js'; -import { useInterval } from '@@/js/use-interval.js'; import { i18n } from '@/i18n.js'; -import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js'; import { defaultStore } from '@/store.js'; const name = 'federation'; @@ -57,6 +56,8 @@ const { widgetProps, configure } = useWidgetPropsManager(name, emit, ); +const mediaProxy = inject('mediaProxy'); + const instances = ref<Misskey.entities.FederationInstance[]>([]); const charts = ref<Misskey.entities.ChartsInstanceResponse[]>([]); const fetching = ref(true); @@ -78,7 +79,7 @@ useInterval(fetch, 1000 * 60, { }); function getInstanceIcon(instance): string { - return getProxiedImageUrlNullable(instance.iconUrl, 'preview') ?? getProxiedImageUrlNullable(instance.faviconUrl, 'preview') ?? '/client-assets/dummy.png'; + return mediaProxy.getProxiedImageUrlNullable(instance.iconUrl, 'preview') ?? mediaProxy.getProxiedImageUrlNullable(instance.faviconUrl, 'preview') ?? '/client-assets/dummy.png'; } defineExpose<WidgetComponentExpose>({ diff --git a/packages/frontend/src/widgets/WidgetInstanceCloud.vue b/packages/frontend/src/widgets/WidgetInstanceCloud.vue index d090372b9a..3712f2911e 100644 --- a/packages/frontend/src/widgets/WidgetInstanceCloud.vue +++ b/packages/frontend/src/widgets/WidgetInstanceCloud.vue @@ -18,16 +18,15 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { shallowRef } from 'vue'; +import { inject, shallowRef } from 'vue'; import * as Misskey from 'misskey-js'; +import { useInterval } from '@@/js/use-interval.js'; import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import MkContainer from '@/components/MkContainer.vue'; import MkTagCloud from '@/components/MkTagCloud.vue'; import * as os from '@/os.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; -import { useInterval } from '@@/js/use-interval.js'; -import { getProxiedImageUrlNullable } from '@/scripts/media-proxy.js'; const name = 'instanceCloud'; @@ -49,6 +48,8 @@ const { widgetProps, configure } = useWidgetPropsManager(name, emit, ); +const mediaProxy = inject('mediaProxy'); + const cloud = shallowRef<InstanceType<typeof MkTagCloud> | null>(); const activeInstances = shallowRef<Misskey.entities.FederationInstance[] | null>(null); @@ -70,7 +71,7 @@ useInterval(() => { }); function getInstanceIcon(instance): string { - return getProxiedImageUrlNullable(instance.iconUrl, 'preview') ?? getProxiedImageUrlNullable(instance.faviconUrl, 'preview') ?? '/client-assets/dummy.png'; + return mediaProxy.getProxiedImageUrlNullable(instance.iconUrl, 'preview') ?? mediaProxy.getProxiedImageUrlNullable(instance.faviconUrl, 'preview') ?? '/client-assets/dummy.png'; } defineExpose<WidgetComponentExpose>({ diff --git a/packages/frontend/src/widgets/WidgetInstanceInfo.vue b/packages/frontend/src/widgets/WidgetInstanceInfo.vue index 5d8beaf9a9..d2a0a64c41 100644 --- a/packages/frontend/src/widgets/WidgetInstanceInfo.vue +++ b/packages/frontend/src/widgets/WidgetInstanceInfo.vue @@ -5,13 +5,13 @@ SPDX-License-Identifier: AGPL-3.0-only <template> <div class="_panel"> - <div :class="$style.container" :style="{ backgroundImage: instance.bannerUrl ? `url(${ instance.bannerUrl })` : null }"> + <div :class="$style.container" :style="{ backgroundImage: serverMetadata.bannerUrl ? `url(${ serverMetadata.bannerUrl })` : null }"> <div :class="$style.iconContainer"> - <img :src="instance.iconUrl ?? instance.faviconUrl ?? '/favicon.ico'" alt="" :class="$style.icon"/> + <img :src="serverMetadata.iconUrl ?? serverMetadata.faviconUrl ?? '/favicon.ico'" alt="" :class="$style.icon"/> </div> <div :class="$style.bodyContainer"> <div :class="$style.body"> - <MkA :class="$style.name" to="/about" behavior="window">{{ instance.name }}</MkA> + <MkA :class="$style.name" to="/about" behavior="window">{{ serverMetadata.name }}</MkA> <div :class="$style.host">{{ host }}</div> </div> </div> @@ -20,10 +20,12 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> +import { inject } from 'vue'; import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import { host } from '@/config.js'; -import { instance } from '@/instance.js'; + +const serverMetadata = inject('serverMetadata'); const name = 'instanceInfo'; diff --git a/packages/frontend/src/widgets/WidgetPhotos.vue b/packages/frontend/src/widgets/WidgetPhotos.vue index 34be8c5e57..64b3b83c29 100644 --- a/packages/frontend/src/widgets/WidgetPhotos.vue +++ b/packages/frontend/src/widgets/WidgetPhotos.vue @@ -22,12 +22,11 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { onUnmounted, ref } from 'vue'; +import { inject, onUnmounted, ref } from 'vue'; import * as Misskey from 'misskey-js'; import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import { useStream } from '@/stream.js'; -import { getStaticImageUrl } from '@/scripts/media-proxy.js'; import { misskeyApi } from '@/scripts/misskey-api.js'; import MkContainer from '@/components/MkContainer.vue'; import { defaultStore } from '@/store.js'; @@ -57,6 +56,8 @@ const { widgetProps, configure } = useWidgetPropsManager(name, emit, ); +const mediaProxy = inject('mediaProxy'); + const connection = useStream().useChannel('main'); const images = ref<Misskey.entities.DriveFile[]>([]); const fetching = ref(true); @@ -70,7 +71,7 @@ const onDriveFileCreated = (file) => { const thumbnail = (image: any): string => { return defaultStore.state.disableShowingAnimatedImages - ? getStaticImageUrl(image.url) + ? mediaProxy.getStaticImageUrl(image.url) : image.thumbnailUrl; }; diff --git a/packages/frontend/src/widgets/WidgetRss.vue b/packages/frontend/src/widgets/WidgetRss.vue index 13f5a4802a..457b6b6bc6 100644 --- a/packages/frontend/src/widgets/WidgetRss.vue +++ b/packages/frontend/src/widgets/WidgetRss.vue @@ -12,7 +12,7 @@ SPDX-License-Identifier: AGPL-3.0-only <div class="ekmkgxbj"> <MkLoading v-if="fetching"/> <div v-else-if="(!items || items.length === 0) && widgetProps.showHeader" class="_fullinfo"> - <img :src="infoImageUrl" class="_ghost"/> + <img v-if="serverMetadata.infoImageUrl" :src="serverMetadata.infoImageUrl" class="_ghost"/> <div>{{ i18n.ts.nothing }}</div> </div> <div v-else :class="$style.feed"> @@ -31,7 +31,8 @@ import MkContainer from '@/components/MkContainer.vue'; import { url as base } from '@/config.js'; import { i18n } from '@/i18n.js'; import { useInterval } from '@@/js/use-interval.js'; -import { infoImageUrl } from '@/instance.js'; +import { inject } from 'vue'; +const serverMetadata = inject('serverMetadata'); const name = 'rss'; diff --git a/packages/frontend/src/widgets/WidgetTimeline.vue b/packages/frontend/src/widgets/WidgetTimeline.vue index d02f9b8e22..57af0e00eb 100644 --- a/packages/frontend/src/widgets/WidgetTimeline.vue +++ b/packages/frontend/src/widgets/WidgetTimeline.vue @@ -17,7 +17,7 @@ SPDX-License-Identifier: AGPL-3.0-only </button> </template> - <div v-if="isBasicTimeline(widgetProps.src) && !isAvailableBasicTimeline(widgetProps.src)" :class="$style.disabled"> + <div v-if="isBasicTimeline(widgetProps.src) && !isAvailableBasicTimeline(serverMetadata, widgetProps.src)" :class="$style.disabled"> <p :class="$style.disabledTitle"> <i class="ti ti-minus"></i> {{ i18n.ts._disabledTimeline.title }} @@ -31,7 +31,7 @@ SPDX-License-Identifier: AGPL-3.0-only </template> <script lang="ts" setup> -import { ref } from 'vue'; +import { inject, ref } from 'vue'; import { useWidgetPropsManager, WidgetComponentEmits, WidgetComponentExpose, WidgetComponentProps } from './widget.js'; import { GetFormResultType } from '@/scripts/form.js'; import * as os from '@/os.js'; @@ -80,6 +80,8 @@ const { widgetProps, configure, save } = useWidgetPropsManager(name, emit, ); +const serverMetadata = inject('serverMetadata'); + const menuOpened = ref(false); const setSrc = (src) => { @@ -109,7 +111,7 @@ const choose = async (ev) => { setSrc('list'); }, })); - os.popupMenu([...availableBasicTimelines().map(tl => ({ + os.popupMenu([...availableBasicTimelines(serverMetadata).map(tl => ({ text: i18n.ts._timelines[tl], icon: basicTimelineIconClass(tl), action: () => { setSrc(tl); },