summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEbise Lutica <7106976+EbiseLutica@users.noreply.github.com>2023-06-09 14:00:53 +0900
committerGitHub <noreply@github.com>2023-06-09 14:00:53 +0900
commit34a32a8334b332de1fd202c9e3fd871d566bab0b (patch)
treec268e17ca8e4eddc61751d79dd9a197be2be2963
parenttweak of 6032c2be1 (diff)
downloadsharkey-34a32a8334b332de1fd202c9e3fd871d566bab0b.tar.gz
sharkey-34a32a8334b332de1fd202c9e3fd871d566bab0b.tar.bz2
sharkey-34a32a8334b332de1fd202c9e3fd871d566bab0b.zip
エラー画像URLを設定可能に (#10959)
* エラー画像URLを設定可能に * Update CHANGELOG.md * 設定したエラーアイコンをprefetchするようにbase.pugを変更 * 不足していたデータを追加 * enhance(frontend): デザイン調整
-rw-r--r--CHANGELOG.md3
-rw-r--r--packages/backend/migration/1685973839966-errorImageUrl.js17
-rw-r--r--packages/backend/src/models/entities/Meta.ts16
-rw-r--r--packages/backend/src/server/api/endpoints/admin/meta.ts15
-rw-r--r--packages/backend/src/server/api/endpoints/admin/update-meta.ts20
-rw-r--r--packages/backend/src/server/api/endpoints/meta.ts17
-rw-r--r--packages/backend/src/server/web/ClientServerService.ts48
-rw-r--r--packages/backend/src/server/web/views/base.pug6
-rw-r--r--packages/frontend/src/components/MkChannelList.vue3
-rw-r--r--packages/frontend/src/components/MkNotes.vue3
-rw-r--r--packages/frontend/src/components/MkNotifications.vue3
-rw-r--r--packages/frontend/src/components/MkPagination.vue4
-rw-r--r--packages/frontend/src/components/MkReactedUsersDialog.vue3
-rw-r--r--packages/frontend/src/components/MkRenotedUsersDialog.vue3
-rw-r--r--packages/frontend/src/components/MkUserList.vue3
-rw-r--r--packages/frontend/src/components/global/MkError.vue3
-rw-r--r--packages/frontend/src/const.ts4
-rw-r--r--packages/frontend/src/instance.ts9
-rw-r--r--packages/frontend/src/pages/_error_.vue3
-rw-r--r--packages/frontend/src/pages/admin/roles.role.vue3
-rw-r--r--packages/frontend/src/pages/admin/settings.vue24
-rw-r--r--packages/frontend/src/pages/favorites.vue3
-rw-r--r--packages/frontend/src/pages/follow-requests.vue3
-rw-r--r--packages/frontend/src/pages/list.vue3
-rw-r--r--packages/frontend/src/pages/not-found.vue3
-rw-r--r--packages/frontend/src/pages/role.vue3
-rw-r--r--packages/frontend/src/pages/settings/apps.vue3
-rw-r--r--packages/frontend/src/pages/settings/mute-block.vue7
-rw-r--r--packages/frontend/src/widgets/WidgetRss.vue3
-rw-r--r--packages/misskey-js/src/entities.ts4
30 files changed, 177 insertions, 65 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 9c4cba38a5..76d3b886db 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -17,6 +17,9 @@
### Client
- Fix: タブがアクティブな間はstreamが切断されないように
+### General
+- エラー時や項目が存在しないときなどのアイコン画像をサーバー管理者が設定できるようになりました
+
### Server
- Fix: api/metaで`TypeError: JSON5.parse is not a function`エラーが発生する問題を修正
diff --git a/packages/backend/migration/1685973839966-errorImageUrl.js b/packages/backend/migration/1685973839966-errorImageUrl.js
new file mode 100644
index 0000000000..fd5d467162
--- /dev/null
+++ b/packages/backend/migration/1685973839966-errorImageUrl.js
@@ -0,0 +1,17 @@
+export class ErrorImageUrl1685973839966 {
+ name = 'ErrorImageUrl1685973839966'
+
+ async up(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "errorImageUrl"`);
+ await queryRunner.query(`ALTER TABLE "meta" ADD "serverErrorImageUrl" character varying(1024)`);
+ await queryRunner.query(`ALTER TABLE "meta" ADD "notFoundImageUrl" character varying(1024)`);
+ await queryRunner.query(`ALTER TABLE "meta" ADD "infoImageUrl" character varying(1024)`);
+ }
+
+ async down(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "infoImageUrl"`);
+ await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "notFoundImageUrl"`);
+ await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "serverErrorImageUrl"`);
+ await queryRunner.query(`ALTER TABLE "meta" ADD "errorImageUrl" character varying(1024) DEFAULT 'https://xn--931a.moe/aiart/yubitun.png'`);
+ }
+}
diff --git a/packages/backend/src/models/entities/Meta.ts b/packages/backend/src/models/entities/Meta.ts
index 6d44e4edc7..f799551f30 100644
--- a/packages/backend/src/models/entities/Meta.ts
+++ b/packages/backend/src/models/entities/Meta.ts
@@ -101,13 +101,25 @@ export class Meta {
length: 1024,
nullable: true,
})
- public errorImageUrl: string | null;
+ public iconUrl: string | null;
@Column('varchar', {
length: 1024,
nullable: true,
})
- public iconUrl: string | null;
+ public serverErrorImageUrl: string | null;
+
+ @Column('varchar', {
+ length: 1024,
+ nullable: true,
+ })
+ public notFoundImageUrl: string | null;
+
+ @Column('varchar', {
+ length: 1024,
+ nullable: true,
+ })
+ public infoImageUrl: string | null;
@Column('boolean', {
default: true,
diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts
index 87a2d22ac2..4cc1b6011f 100644
--- a/packages/backend/src/server/api/endpoints/admin/meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/meta.ts
@@ -61,10 +61,17 @@ export const meta = {
type: 'string',
optional: false, nullable: true,
},
- errorImageUrl: {
+ serverErrorImageUrl: {
+ type: 'string',
+ optional: false, nullable: true,
+ },
+ infoImageUrl: {
+ type: 'string',
+ optional: false, nullable: true,
+ },
+ notFoundImageUrl: {
type: 'string',
optional: false, nullable: true,
- default: 'https://xn--931a.moe/aiart/yubitun.png',
},
iconUrl: {
type: 'string',
@@ -305,7 +312,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
themeColor: instance.themeColor,
mascotImageUrl: instance.mascotImageUrl,
bannerUrl: instance.bannerUrl,
- errorImageUrl: instance.errorImageUrl,
+ serverErrorImageUrl: instance.serverErrorImageUrl,
+ notFoundImageUrl: instance.notFoundImageUrl,
+ infoImageUrl: instance.infoImageUrl,
iconUrl: instance.iconUrl,
backgroundImageUrl: instance.backgroundImageUrl,
logoImageUrl: instance.logoImageUrl,
diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts
index 0e94f56cfd..1de5e9efd3 100644
--- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts
@@ -32,7 +32,9 @@ export const paramDef = {
themeColor: { type: 'string', nullable: true, pattern: '^#[0-9a-fA-F]{6}$' },
mascotImageUrl: { type: 'string', nullable: true },
bannerUrl: { type: 'string', nullable: true },
- errorImageUrl: { type: 'string', nullable: true },
+ serverErrorImageUrl: { type: 'string', nullable: true },
+ infoImageUrl: { type: 'string', nullable: true },
+ notFoundImageUrl: { type: 'string', nullable: true },
iconUrl: { type: 'string', nullable: true },
backgroundImageUrl: { type: 'string', nullable: true },
logoImageUrl: { type: 'string', nullable: true },
@@ -149,6 +151,18 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
set.iconUrl = ps.iconUrl;
}
+ if (ps.serverErrorImageUrl !== undefined) {
+ set.serverErrorImageUrl = ps.serverErrorImageUrl;
+ }
+
+ if (ps.infoImageUrl !== undefined) {
+ set.infoImageUrl = ps.infoImageUrl;
+ }
+
+ if (ps.notFoundImageUrl !== undefined) {
+ set.notFoundImageUrl = ps.notFoundImageUrl;
+ }
+
if (ps.backgroundImageUrl !== undefined) {
set.backgroundImageUrl = ps.backgroundImageUrl;
}
@@ -281,10 +295,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
set.smtpPass = ps.smtpPass;
}
- if (ps.errorImageUrl !== undefined) {
- set.errorImageUrl = ps.errorImageUrl;
- }
-
if (ps.enableServiceWorker !== undefined) {
set.enableServiceWorker = ps.enableServiceWorker;
}
diff --git a/packages/backend/src/server/api/endpoints/meta.ts b/packages/backend/src/server/api/endpoints/meta.ts
index fe68467a64..3b3c5caa00 100644
--- a/packages/backend/src/server/api/endpoints/meta.ts
+++ b/packages/backend/src/server/api/endpoints/meta.ts
@@ -124,10 +124,17 @@ export const meta = {
type: 'string',
optional: false, nullable: false,
},
- errorImageUrl: {
+ serverErrorImageUrl: {
type: 'string',
- optional: false, nullable: false,
- default: 'https://xn--931a.moe/aiart/yubitun.png',
+ optional: false, nullable: true,
+ },
+ infoImageUrl: {
+ type: 'string',
+ optional: false, nullable: true,
+ },
+ notFoundImageUrl: {
+ type: 'string',
+ optional: false, nullable: true,
},
iconUrl: {
type: 'string',
@@ -288,7 +295,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> {
themeColor: instance.themeColor,
mascotImageUrl: instance.mascotImageUrl,
bannerUrl: instance.bannerUrl,
- errorImageUrl: instance.errorImageUrl,
+ infoImageUrl: instance.infoImageUrl,
+ serverErrorImageUrl: instance.serverErrorImageUrl,
+ notFoundImageUrl: instance.notFoundImageUrl,
iconUrl: instance.iconUrl,
backgroundImageUrl: instance.backgroundImageUrl,
logoImageUrl: instance.logoImageUrl,
diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts
index f780280c1f..07ba2731c3 100644
--- a/packages/backend/src/server/web/ClientServerService.ts
+++ b/packages/backend/src/server/web/ClientServerService.ts
@@ -26,7 +26,7 @@ import { PageEntityService } from '@/core/entities/PageEntityService.js';
import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js';
import { ClipEntityService } from '@/core/entities/ClipEntityService.js';
import { ChannelEntityService } from '@/core/entities/ChannelEntityService.js';
-import type { ChannelsRepository, ClipsRepository, FlashsRepository, GalleryPostsRepository, NotesRepository, PagesRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js';
+import type { ChannelsRepository, ClipsRepository, FlashsRepository, GalleryPostsRepository, Meta, NotesRepository, PagesRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js';
import type Logger from '@/logger.js';
import { deepClone } from '@/misc/clone.js';
import { bindThis } from '@/decorators.js';
@@ -118,6 +118,18 @@ export class ClientServerService {
}
@bindThis
+ private generateCommonPugData(meta: Meta) {
+ return {
+ instanceName: meta.name ?? 'Misskey',
+ icon: meta.iconUrl,
+ themeColor: meta.themeColor,
+ serverErrorImageUrl: meta.serverErrorImageUrl ?? 'https://xn--931a.moe/assets/error.jpg',
+ infoImageUrl: meta.infoImageUrl ?? 'https://xn--931a.moe/assets/info.jpg',
+ notFoundImageUrl: meta.notFoundImageUrl ?? 'https://xn--931a.moe/assets/not-found.jpg',
+ };
+ }
+
+ @bindThis
public createServer(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) {
fastify.register(fastifyCookie, {});
@@ -341,12 +353,10 @@ export class ClientServerService {
reply.header('Cache-Control', 'public, max-age=30');
return await reply.view('base', {
img: meta.bannerUrl,
- title: meta.name ?? 'Misskey',
- instanceName: meta.name ?? 'Misskey',
url: this.config.url,
+ title: meta.name ?? 'Misskey',
desc: meta.description,
- icon: meta.iconUrl,
- themeColor: meta.themeColor,
+ ...this.generateCommonPugData(meta),
});
};
@@ -431,9 +441,7 @@ export class ClientServerService {
user, profile, me,
avatarUrl: user.avatarUrl ?? this.userEntityService.getIdenticonUrl(user),
sub: request.params.sub,
- instanceName: meta.name ?? 'Misskey',
- icon: meta.iconUrl,
- themeColor: meta.themeColor,
+ ...this.generateCommonPugData(meta),
});
} else {
// リモートユーザーなので
@@ -481,9 +489,7 @@ export class ClientServerService {
avatarUrl: _note.user.avatarUrl,
// TODO: Let locale changeable by instance setting
summary: getNoteSummary(_note),
- instanceName: meta.name ?? 'Misskey',
- icon: meta.iconUrl,
- themeColor: meta.themeColor,
+ ...this.generateCommonPugData(meta),
});
} else {
return await renderBase(reply);
@@ -522,9 +528,7 @@ export class ClientServerService {
page: _page,
profile,
avatarUrl: _page.user.avatarUrl,
- instanceName: meta.name ?? 'Misskey',
- icon: meta.iconUrl,
- themeColor: meta.themeColor,
+ ...this.generateCommonPugData(meta),
});
} else {
return await renderBase(reply);
@@ -550,9 +554,7 @@ export class ClientServerService {
flash: _flash,
profile,
avatarUrl: _flash.user.avatarUrl,
- instanceName: meta.name ?? 'Misskey',
- icon: meta.iconUrl,
- themeColor: meta.themeColor,
+ ...this.generateCommonPugData(meta),
});
} else {
return await renderBase(reply);
@@ -578,9 +580,7 @@ export class ClientServerService {
clip: _clip,
profile,
avatarUrl: _clip.user.avatarUrl,
- instanceName: meta.name ?? 'Misskey',
- icon: meta.iconUrl,
- themeColor: meta.themeColor,
+ ...this.generateCommonPugData(meta),
});
} else {
return await renderBase(reply);
@@ -604,9 +604,7 @@ export class ClientServerService {
post: _post,
profile,
avatarUrl: _post.user.avatarUrl,
- instanceName: meta.name ?? 'Misskey',
- icon: meta.iconUrl,
- themeColor: meta.themeColor,
+ ...this.generateCommonPugData(meta),
});
} else {
return await renderBase(reply);
@@ -625,9 +623,7 @@ export class ClientServerService {
reply.header('Cache-Control', 'public, max-age=15');
return await reply.view('channel', {
channel: _channel,
- instanceName: meta.name ?? 'Misskey',
- icon: meta.iconUrl,
- themeColor: meta.themeColor,
+ ...this.generateCommonPugData(meta),
});
} else {
return await renderBase(reply);
diff --git a/packages/backend/src/server/web/views/base.pug b/packages/backend/src/server/web/views/base.pug
index 69b3f68e05..1216fc73f7 100644
--- a/packages/backend/src/server/web/views/base.pug
+++ b/packages/backend/src/server/web/views/base.pug
@@ -31,9 +31,9 @@ html
link(rel='apple-touch-icon' href= icon || '/apple-touch-icon.png')
link(rel='manifest' href='/manifest.json')
link(rel='search' type='application/opensearchdescription+xml' title=(title || "Misskey") href=`${url}/opensearch.xml`)
- link(rel='prefetch' href='https://xn--931a.moe/assets/info.jpg')
- link(rel='prefetch' href='https://xn--931a.moe/assets/not-found.jpg')
- link(rel='prefetch' href='https://xn--931a.moe/assets/error.jpg')
+ link(rel='prefetch' href=serverErrorImageUrl)
+ link(rel='prefetch' href=infoImageUrl)
+ link(rel='prefetch' href=notFoundImageUrl)
//- https://github.com/misskey-dev/misskey/issues/9842
link(rel='stylesheet' href='/assets/tabler-icons/tabler-icons.min.css?v2.21.0')
link(rel='modulepreload' href=`/vite/${clientEntry.file}`)
diff --git a/packages/frontend/src/components/MkChannelList.vue b/packages/frontend/src/components/MkChannelList.vue
index 4050520eb9..2d3ea8d177 100644
--- a/packages/frontend/src/components/MkChannelList.vue
+++ b/packages/frontend/src/components/MkChannelList.vue
@@ -2,7 +2,7 @@
<MkPagination :pagination="pagination">
<template #empty>
<div class="_fullinfo">
- <img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
+ <img :src="infoImageUrl" class="_ghost"/>
<div>{{ i18n.ts.notFound }}</div>
</div>
</template>
@@ -17,6 +17,7 @@
import MkChannelPreview from '@/components/MkChannelPreview.vue';
import MkPagination, { Paging } from '@/components/MkPagination.vue';
import { i18n } from '@/i18n';
+import { infoImageUrl } from '@/instance';
const props = withDefaults(defineProps<{
pagination: Paging;
diff --git a/packages/frontend/src/components/MkNotes.vue b/packages/frontend/src/components/MkNotes.vue
index 9cc2b7a967..b49c8fa8b7 100644
--- a/packages/frontend/src/components/MkNotes.vue
+++ b/packages/frontend/src/components/MkNotes.vue
@@ -2,7 +2,7 @@
<MkPagination ref="pagingComponent" :pagination="pagination">
<template #empty>
<div class="_fullinfo">
- <img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
+ <img :src="infoImageUrl" class="_ghost"/>
<div>{{ i18n.ts.noNotes }}</div>
</div>
</template>
@@ -32,6 +32,7 @@ import MkNote from '@/components/MkNote.vue';
import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue';
import MkPagination, { Paging } from '@/components/MkPagination.vue';
import { i18n } from '@/i18n';
+import { infoImageUrl } from '@/instance';
const props = defineProps<{
pagination: Paging;
diff --git a/packages/frontend/src/components/MkNotifications.vue b/packages/frontend/src/components/MkNotifications.vue
index 70224bffa1..d4a30d1916 100644
--- a/packages/frontend/src/components/MkNotifications.vue
+++ b/packages/frontend/src/components/MkNotifications.vue
@@ -2,7 +2,7 @@
<MkPagination ref="pagingComponent" :pagination="pagination">
<template #empty>
<div class="_fullinfo">
- <img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
+ <img :src="infoImageUrl" class="_ghost"/>
<div>{{ i18n.ts.noNotifications }}</div>
</div>
</template>
@@ -26,6 +26,7 @@ import { useStream } from '@/stream';
import { $i } from '@/account';
import { i18n } from '@/i18n';
import { notificationTypes } from '@/const';
+import { infoImageUrl } from '@/instance';
const props = defineProps<{
includeTypes?: typeof notificationTypes[number][];
diff --git a/packages/frontend/src/components/MkPagination.vue b/packages/frontend/src/components/MkPagination.vue
index 740094b113..598529bf58 100644
--- a/packages/frontend/src/components/MkPagination.vue
+++ b/packages/frontend/src/components/MkPagination.vue
@@ -13,7 +13,7 @@
<div v-else-if="empty" key="_empty_" class="empty">
<slot name="empty">
<div class="_fullinfo">
- <img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
+ <img :src="infoImageUrl" class="_ghost"/>
<div>{{ i18n.ts.nothing }}</div>
</div>
</slot>
@@ -73,6 +73,8 @@ export type Paging<E extends keyof misskey.Endpoints = keyof misskey.Endpoints>
};
</script>
<script lang="ts" setup>
+import { infoImageUrl } from '@/instance';
+
const props = withDefaults(defineProps<{
pagination: Paging;
disableAutoLoad?: boolean;
diff --git a/packages/frontend/src/components/MkReactedUsersDialog.vue b/packages/frontend/src/components/MkReactedUsersDialog.vue
index cd2a359d5c..0a858a8965 100644
--- a/packages/frontend/src/components/MkReactedUsersDialog.vue
+++ b/packages/frontend/src/components/MkReactedUsersDialog.vue
@@ -11,7 +11,7 @@
<MkSpacer :marginMin="20" :marginMax="28">
<div v-if="note" class="_gaps">
<div v-if="reactions.length === 0" class="_fullinfo">
- <img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
+ <img :src="infoImageUrl" class="_ghost"/>
<div>{{ i18n.ts.nothing }}</div>
</div>
<template v-else>
@@ -42,6 +42,7 @@ import MkUserCardMini from '@/components/MkUserCardMini.vue';
import { userPage } from '@/filters/user';
import { i18n } from '@/i18n';
import * as os from '@/os';
+import { infoImageUrl } from '@/instance';
const emit = defineEmits<{
(ev: 'closed'): void,
diff --git a/packages/frontend/src/components/MkRenotedUsersDialog.vue b/packages/frontend/src/components/MkRenotedUsersDialog.vue
index 814a68d4da..484cb2f9a7 100644
--- a/packages/frontend/src/components/MkRenotedUsersDialog.vue
+++ b/packages/frontend/src/components/MkRenotedUsersDialog.vue
@@ -11,7 +11,7 @@
<MkSpacer :marginMin="20" :marginMax="28">
<div v-if="renotes" class="_gaps">
<div v-if="renotes.length === 0" class="_fullinfo">
- <img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
+ <img :src="infoImageUrl" class="_ghost"/>
<div>{{ i18n.ts.nothing }}</div>
</div>
<template v-else>
@@ -35,6 +35,7 @@ import MkUserCardMini from '@/components/MkUserCardMini.vue';
import { userPage } from '@/filters/user';
import { i18n } from '@/i18n';
import * as os from '@/os';
+import { infoImageUrl } from '@/instance';
const emit = defineEmits<{
(ev: 'closed'): void,
diff --git a/packages/frontend/src/components/MkUserList.vue b/packages/frontend/src/components/MkUserList.vue
index 3571ca84d9..2a23f3e70d 100644
--- a/packages/frontend/src/components/MkUserList.vue
+++ b/packages/frontend/src/components/MkUserList.vue
@@ -2,7 +2,7 @@
<MkPagination :pagination="pagination">
<template #empty>
<div class="_fullinfo">
- <img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
+ <img :src="infoImageUrl" class="_ghost"/>
<div>{{ i18n.ts.noUsers }}</div>
</div>
</template>
@@ -19,6 +19,7 @@
import MkUserInfo from '@/components/MkUserInfo.vue';
import MkPagination, { Paging } from '@/components/MkPagination.vue';
import { i18n } from '@/i18n';
+import { infoImageUrl } from '@/instance';
const props = withDefaults(defineProps<{
pagination: Paging;
diff --git a/packages/frontend/src/components/global/MkError.vue b/packages/frontend/src/components/global/MkError.vue
index 513ef21d35..24b0835135 100644
--- a/packages/frontend/src/components/global/MkError.vue
+++ b/packages/frontend/src/components/global/MkError.vue
@@ -1,7 +1,7 @@
<template>
<Transition :name="defaultStore.state.animation ? '_transition_zoom' : ''" appear>
<div :class="$style.root">
- <img :class="$style.img" src="https://xn--931a.moe/assets/error.jpg" class="_ghost"/>
+ <img :class="$style.img" :src="infoImageUrl" 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>
@@ -12,6 +12,7 @@
import MkButton from '@/components/MkButton.vue';
import { i18n } from '@/i18n';
import { defaultStore } from '@/store';
+import { infoImageUrl } from '@/instance';
const emit = defineEmits<{
(ev: 'retry'): void;
diff --git a/packages/frontend/src/const.ts b/packages/frontend/src/const.ts
index aaa3d10302..ad7fa372e9 100644
--- a/packages/frontend/src/const.ts
+++ b/packages/frontend/src/const.ts
@@ -78,3 +78,7 @@ export const ROLE_POLICIES = [
//export const CURRENT_STICKY_BOTTOM = Symbol('CURRENT_STICKY_BOTTOM');
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';
diff --git a/packages/frontend/src/instance.ts b/packages/frontend/src/instance.ts
index f4c1988704..9cfcbcbc3f 100644
--- a/packages/frontend/src/instance.ts
+++ b/packages/frontend/src/instance.ts
@@ -1,7 +1,8 @@
-import { reactive } from 'vue';
+import { computed, reactive } from 'vue';
import * as Misskey from 'misskey-js';
import { api } from './os';
import { miLocalStorage } from './local-storage';
+import { DEFAULT_INFO_IMAGE_URL, DEFAULT_NOT_FOUND_IMAGE_URL, DEFAULT_SERVER_ERROR_IMAGE_URL } from '@/const';
// TODO: 他のタブと永続化されたstateを同期
@@ -13,6 +14,12 @@ export const instance: Misskey.entities.InstanceMetadata = reactive(cached ? JSO
// TODO: set default values
});
+export const serverErrorImageUrl = computed(() => instance.serverErrorImageUrl ?? DEFAULT_SERVER_ERROR_IMAGE_URL);
+
+export const infoImageUrl = computed(() => instance.infoImageUrl ?? DEFAULT_INFO_IMAGE_URL);
+
+export const notFoundImageUrl = computed(() => instance.notFoundImageUrl ?? DEFAULT_NOT_FOUND_IMAGE_URL);
+
export async function fetchInstance() {
const meta = await api('meta', {
detail: false,
diff --git a/packages/frontend/src/pages/_error_.vue b/packages/frontend/src/pages/_error_.vue
index f27d2df336..eee661bd8a 100644
--- a/packages/frontend/src/pages/_error_.vue
+++ b/packages/frontend/src/pages/_error_.vue
@@ -2,7 +2,7 @@
<MkLoading v-if="!loaded"/>
<Transition :name="defaultStore.state.animation ? '_transition_zoom' : ''" appear>
<div v-show="loaded" :class="$style.root">
- <img src="https://xn--931a.moe/assets/error.jpg" class="_ghost" :class="$style.img"/>
+ <img :src="serverErrorImageUrl" class="_ghost" :class="$style.img"/>
<div class="_gaps">
<p><b><i class="ti ti-alert-triangle"></i> {{ i18n.ts.pageLoadError }}</b></p>
<p v-if="meta && (version === meta.version)">{{ i18n.ts.pageLoadErrorDescription }}</p>
@@ -30,6 +30,7 @@ import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata';
import { miLocalStorage } from '@/local-storage';
import { defaultStore } from '@/store';
+import { serverErrorImageUrl } from '@/instance';
const props = withDefaults(defineProps<{
error?: Error;
diff --git a/packages/frontend/src/pages/admin/roles.role.vue b/packages/frontend/src/pages/admin/roles.role.vue
index 4ed6abf200..6cbe7ae658 100644
--- a/packages/frontend/src/pages/admin/roles.role.vue
+++ b/packages/frontend/src/pages/admin/roles.role.vue
@@ -23,7 +23,7 @@
<MkPagination :pagination="usersPagination">
<template #empty>
<div class="_fullinfo">
- <img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
+ <img :src="infoImageUrl" class="_ghost"/>
<div>{{ i18n.ts.noUsers }}</div>
</div>
</template>
@@ -69,6 +69,7 @@ import MkButton from '@/components/MkButton.vue';
import MkUserCardMini from '@/components/MkUserCardMini.vue';
import MkInfo from '@/components/MkInfo.vue';
import MkPagination, { Paging } from '@/components/MkPagination.vue';
+import { infoImageUrl } from '@/instance';
const router = useRouter();
diff --git a/packages/frontend/src/pages/admin/settings.vue b/packages/frontend/src/pages/admin/settings.vue
index 39d5ae8607..e98e1432a2 100644
--- a/packages/frontend/src/pages/admin/settings.vue
+++ b/packages/frontend/src/pages/admin/settings.vue
@@ -48,6 +48,21 @@
<template #label>{{ i18n.ts.backgroundImageUrl }}</template>
</MkInput>
+ <MkInput v-model="notFoundImageUrl">
+ <template #prefix><i class="ti ti-link"></i></template>
+ <template #label>{{ i18n.ts.notFoundDescription }}</template>
+ </MkInput>
+
+ <MkInput v-model="infoImageUrl">
+ <template #prefix><i class="ti ti-link"></i></template>
+ <template #label>{{ i18n.ts.nothing }}</template>
+ </MkInput>
+
+ <MkInput v-model="serverErrorImageUrl">
+ <template #prefix><i class="ti ti-link"></i></template>
+ <template #label>{{ i18n.ts.somethingHappened }}</template>
+ </MkInput>
+
<MkColorInput v-model="themeColor">
<template #label>{{ i18n.ts.themeColor }}</template>
</MkColorInput>
@@ -151,6 +166,9 @@ let backgroundImageUrl: string | null = $ref(null);
let themeColor: any = $ref(null);
let defaultLightTheme: any = $ref(null);
let defaultDarkTheme: any = $ref(null);
+let serverErrorImageUrl: string | null = $ref(null);
+let infoImageUrl: string | null = $ref(null);
+let notFoundImageUrl: string | null = $ref(null);
let pinnedUsers: string = $ref('');
let cacheRemoteFiles: boolean = $ref(false);
let enableServiceWorker: boolean = $ref(false);
@@ -169,6 +187,9 @@ async function init() {
themeColor = meta.themeColor;
defaultLightTheme = meta.defaultLightTheme;
defaultDarkTheme = meta.defaultDarkTheme;
+ serverErrorImageUrl = meta.serverErrorImageUrl;
+ infoImageUrl = meta.infoImageUrl;
+ notFoundImageUrl = meta.notFoundImageUrl;
maintainerName = meta.maintainerName;
maintainerEmail = meta.maintainerEmail;
pinnedUsers = meta.pinnedUsers.join('\n');
@@ -190,6 +211,9 @@ function save() {
themeColor: themeColor === '' ? null : themeColor,
defaultLightTheme: defaultLightTheme === '' ? null : defaultLightTheme,
defaultDarkTheme: defaultDarkTheme === '' ? null : defaultDarkTheme,
+ infoImageUrl,
+ notFoundImageUrl,
+ serverErrorImageUrl,
maintainerName,
maintainerEmail,
pinnedUsers: pinnedUsers.split('\n'),
diff --git a/packages/frontend/src/pages/favorites.vue b/packages/frontend/src/pages/favorites.vue
index 460bf65d1e..21c306148b 100644
--- a/packages/frontend/src/pages/favorites.vue
+++ b/packages/frontend/src/pages/favorites.vue
@@ -5,7 +5,7 @@
<MkPagination :pagination="pagination">
<template #empty>
<div class="_fullinfo">
- <img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
+ <img :src="infoImageUrl" class="_ghost"/>
<div>{{ i18n.ts.noNotes }}</div>
</div>
</template>
@@ -26,6 +26,7 @@ import MkNote from '@/components/MkNote.vue';
import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue';
import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata';
+import { infoImageUrl } from '@/instance';
const pagination = {
endpoint: 'i/favorites' as const,
diff --git a/packages/frontend/src/pages/follow-requests.vue b/packages/frontend/src/pages/follow-requests.vue
index 1452942a1e..a70a4894a5 100644
--- a/packages/frontend/src/pages/follow-requests.vue
+++ b/packages/frontend/src/pages/follow-requests.vue
@@ -5,7 +5,7 @@
<MkPagination ref="paginationComponent" :pagination="pagination">
<template #empty>
<div class="_fullinfo">
- <img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
+ <img :src="infoImageUrl" class="_ghost"/>
<div>{{ i18n.ts.noFollowRequests }}</div>
</div>
</template>
@@ -39,6 +39,7 @@ import { userPage, acct } from '@/filters/user';
import * as os from '@/os';
import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata';
+import { infoImageUrl } from '@/instance';
const paginationComponent = shallowRef<InstanceType<typeof MkPagination>>();
diff --git a/packages/frontend/src/pages/list.vue b/packages/frontend/src/pages/list.vue
index f92c06d1c5..40934fb71d 100644
--- a/packages/frontend/src/pages/list.vue
+++ b/packages/frontend/src/pages/list.vue
@@ -3,7 +3,7 @@
<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="https://xn--931a.moe/assets/error.jpg" class="_ghost"/>
+ <img :class="$style.img" :src="serverErrorImageUrl" class="_ghost"/>
<p :class="$style.text">
<i class="ti ti-alert-triangle"></i>
{{ i18n.ts.nothing }}
@@ -36,6 +36,7 @@ import { i18n } from '@/i18n';
import MkUserCardMini from '@/components/MkUserCardMini.vue';
import MkButton from '@/components/MkButton.vue';
import { definePageMetadata } from '@/scripts/page-metadata';
+import { serverErrorImageUrl } from '@/instance';
const props = defineProps<{
listId: string;
diff --git a/packages/frontend/src/pages/not-found.vue b/packages/frontend/src/pages/not-found.vue
index 2c9d949017..43dc41e7cc 100644
--- a/packages/frontend/src/pages/not-found.vue
+++ b/packages/frontend/src/pages/not-found.vue
@@ -1,7 +1,7 @@
<template>
<div>
<div class="_fullinfo">
- <img src="https://xn--931a.moe/assets/not-found.jpg" class="_ghost"/>
+ <img :src="notFoundImageUrl" class="_ghost"/>
<div>{{ i18n.ts.notFoundDescription }}</div>
</div>
</div>
@@ -10,6 +10,7 @@
<script lang="ts" setup>
import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata';
+import { notFoundImageUrl } from '@/instance';
const headerActions = $computed(() => []);
diff --git a/packages/frontend/src/pages/role.vue b/packages/frontend/src/pages/role.vue
index e85ab0917a..fc04468d5a 100644
--- a/packages/frontend/src/pages/role.vue
+++ b/packages/frontend/src/pages/role.vue
@@ -3,7 +3,7 @@
<template #header><MkPageHeader v-model:tab="tab" :tabs="headerTabs"/></template>
<MKSpacer v-if="!(typeof error === 'undefined')" :contentMax="1200">
<div :class="$style.root">
- <img :class="$style.img" src="https://xn--931a.moe/assets/error.jpg" class="_ghost"/>
+ <img :class="$style.img" :src="serverErrorImageUrl" class="_ghost"/>
<p :class="$style.text">
<i class="ti ti-alert-triangle"></i>
{{ error }}
@@ -30,6 +30,7 @@ import { definePageMetadata } from '@/scripts/page-metadata';
import { i18n } from '@/i18n';
import MkTimeline from '@/components/MkTimeline.vue';
import { instanceName } from '@/config';
+import { serverErrorImageUrl } from '@/instance';
const props = withDefaults(defineProps<{
role: string;
diff --git a/packages/frontend/src/pages/settings/apps.vue b/packages/frontend/src/pages/settings/apps.vue
index fbb78200d4..cadce49230 100644
--- a/packages/frontend/src/pages/settings/apps.vue
+++ b/packages/frontend/src/pages/settings/apps.vue
@@ -3,7 +3,7 @@
<FormPagination ref="list" :pagination="pagination">
<template #empty>
<div class="_fullinfo">
- <img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
+ <img :src="infoImageUrl" class="_ghost"/>
<div>{{ i18n.ts.nothing }}</div>
</div>
</template>
@@ -47,6 +47,7 @@ import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata';
import MkKeyValue from '@/components/MkKeyValue.vue';
import MkButton from '@/components/MkButton.vue';
+import { infoImageUrl } from '@/instance';
const list = ref<any>(null);
diff --git a/packages/frontend/src/pages/settings/mute-block.vue b/packages/frontend/src/pages/settings/mute-block.vue
index 3d0463f708..e0785ab9fe 100644
--- a/packages/frontend/src/pages/settings/mute-block.vue
+++ b/packages/frontend/src/pages/settings/mute-block.vue
@@ -10,7 +10,7 @@
<MkPagination :pagination="renoteMutingPagination">
<template #empty>
<div class="_fullinfo">
- <img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
+ <img :src="infoImageUrl" class="_ghost"/>
<div>{{ i18n.ts.noUsers }}</div>
</div>
</template>
@@ -38,7 +38,7 @@
<MkPagination :pagination="mutingPagination">
<template #empty>
<div class="_fullinfo">
- <img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
+ <img :src="infoImageUrl" class="_ghost"/>
<div>{{ i18n.ts.noUsers }}</div>
</div>
</template>
@@ -68,7 +68,7 @@
<MkPagination :pagination="blockingPagination">
<template #empty>
<div class="_fullinfo">
- <img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
+ <img :src="infoImageUrl" class="_ghost"/>
<div>{{ i18n.ts.noUsers }}</div>
</div>
</template>
@@ -107,6 +107,7 @@ import { i18n } from '@/i18n';
import { definePageMetadata } from '@/scripts/page-metadata';
import MkUserCardMini from '@/components/MkUserCardMini.vue';
import * as os from '@/os';
+import { infoImageUrl } from '@/instance';
let tab = $ref('renoteMute');
diff --git a/packages/frontend/src/widgets/WidgetRss.vue b/packages/frontend/src/widgets/WidgetRss.vue
index 1be882c66d..953ce2908d 100644
--- a/packages/frontend/src/widgets/WidgetRss.vue
+++ b/packages/frontend/src/widgets/WidgetRss.vue
@@ -7,7 +7,7 @@
<div class="ekmkgxbj">
<MkLoading v-if="fetching"/>
<div v-else-if="(!items || items.length === 0) && widgetProps.showHeader" class="_fullinfo">
- <img src="https://xn--931a.moe/assets/info.jpg" class="_ghost"/>
+ <img :src="infoImageUrl" class="_ghost"/>
<div>{{ i18n.ts.nothing }}</div>
</div>
<div v-else :class="$style.feed">
@@ -25,6 +25,7 @@ import MkContainer from '@/components/MkContainer.vue';
import { url as base } from '@/config';
import { i18n } from '@/i18n';
import { useInterval } from '@/scripts/use-interval';
+import { infoImageUrl } from '@/instance';
const name = 'rss';
diff --git a/packages/misskey-js/src/entities.ts b/packages/misskey-js/src/entities.ts
index 04065c51c9..383b17f0b9 100644
--- a/packages/misskey-js/src/entities.ts
+++ b/packages/misskey-js/src/entities.ts
@@ -294,7 +294,9 @@ export type LiteInstanceMetadata = {
themeColor: string | null;
mascotImageUrl: string | null;
bannerUrl: string | null;
- errorImageUrl: string | null;
+ serverErrorImageUrl: string | null;
+ infoImageUrl: string | null;
+ notFoundImageUrl: string | null;
iconUrl: string | null;
backgroundImageUrl: string | null;
logoImageUrl: string | null;