This commit is contained in:
syuilo 2024-09-01 08:44:55 +09:00
parent e7171d9ab2
commit aab1c76981
117 changed files with 587 additions and 568 deletions

View File

@ -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,

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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';

View File

@ -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',
})}`;
}

View File

@ -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='],

View File

@ -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',
})}`;
}
}

View File

@ -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,10 +175,9 @@ 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));
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) {
@ -190,7 +186,6 @@ export async function common(createVue: () => App<Element>) {
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);

View File

@ -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(),
});

View File

@ -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;

View File

@ -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;

View File

@ -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 {

View File

@ -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;

View File

@ -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>

View File

@ -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;

View File

@ -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;

View File

@ -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>

View File

@ -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';

View File

@ -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,

View File

@ -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,
);

View File

@ -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>

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;

View File

@ -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();

View File

@ -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';

View File

@ -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<{

View File

@ -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 => {

View File

@ -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) {

View File

@ -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,

View File

@ -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 {

View File

@ -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);

View File

@ -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;
}>();

View File

@ -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;

View File

@ -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;
}>();

View File

@ -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;
}>();

View File

@ -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;

View File

@ -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);
}

View File

@ -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, () => {

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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;
});

View File

@ -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;

View File

@ -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,

View File

@ -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'));

View File

@ -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='],

View File

@ -4,7 +4,6 @@
*/
export type Keys =
'v' |
'lastVersion' |
'instance' |
'instanceCachedAt' |

View File

@ -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;

View File

@ -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',

View File

@ -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>

View File

@ -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>

View File

@ -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);
});
}

View File

@ -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);
});
}

View File

@ -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);
});
}

View File

@ -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,

View File

@ -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);
});
}

View File

@ -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);
});
}

View File

@ -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);
});
}

View File

@ -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);
});
}

View File

@ -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);
});
}

View File

@ -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],
};
}
}

View File

@ -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();

View File

@ -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() {

View File

@ -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);
});
}

View File

@ -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 => {

View File

@ -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(() => []);

View File

@ -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,

View File

@ -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',

View File

@ -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';

View File

@ -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,

View File

@ -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>>();

View File

@ -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 ?? '';
}

View File

@ -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,

View File

@ -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;

View File

@ -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 ?? []);

View File

@ -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();

View File

@ -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;

View File

@ -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<{

View File

@ -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;

View File

@ -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 ?? '';
});

View File

@ -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,

View File

@ -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>>();

View File

@ -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();

View File

@ -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',

View File

@ -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';

View File

@ -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));

View File

@ -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),

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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', {

View File

@ -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;
});

View File

@ -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';
}

View File

@ -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();
}

View File

@ -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;
},

View File

@ -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: () => {

View File

@ -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',
})}`;
}

View File

@ -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) {
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 Promise.reject();
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);
});
}));
}

Some files were not shown because too many files have changed in this diff Show More