@@ -180,6 +180,8 @@ function onMouseenter() {
let lastClickTime: number | null = null;
function onMousedown(ev: MouseEvent | TouchEvent) {
+ if (props.disabled) return; // Prevent interaction if disabled
+
ev.preventDefault();
tooltipForDragShowing.value = true;
@@ -292,6 +294,11 @@ function onMousedown(ev: MouseEvent | TouchEvent) {
border: solid 1px var(--MI_THEME-panel);
border-radius: 6px;
+ &.disabled {
+ pointer-events: none;
+ opacity: 0.6;
+ }
+
> .container {
flex: 1;
position: relative;
diff --git a/packages/frontend/src/components/MkReactionIcon.vue b/packages/frontend/src/components/MkReactionIcon.vue
index 36d1103549..7d62456e03 100644
--- a/packages/frontend/src/components/MkReactionIcon.vue
+++ b/packages/frontend/src/components/MkReactionIcon.vue
@@ -24,6 +24,7 @@ const elRef = useTemplateRef('elRef');
if (props.withTooltip) {
useTooltip(elRef, (showing) => {
+ if (elRef.value == null) return;
const { dispose } = os.popup(defineAsyncComponent(() => import('@/components/MkReactionTooltip.vue')), {
showing,
reaction: props.reaction.replace(/^:(\w+):$/, ':$1@.:'),
diff --git a/packages/frontend/src/components/MkRolePreview.vue b/packages/frontend/src/components/MkRolePreview.vue
index 3f14c5b5e0..15149b3f0c 100644
--- a/packages/frontend/src/components/MkRolePreview.vue
+++ b/packages/frontend/src/components/MkRolePreview.vue
@@ -41,7 +41,7 @@ import { i18n } from '@/i18n.js';
const props = withDefaults(defineProps<{
role: Misskey.entities.Role;
forModeration: boolean;
- detailed: boolean;
+ detailed?: boolean;
}>(), {
detailed: true,
});
diff --git a/packages/frontend/src/components/MkSelect.vue b/packages/frontend/src/components/MkSelect.vue
index 58a4edfddf..485d163ac4 100644
--- a/packages/frontend/src/components/MkSelect.vue
+++ b/packages/frontend/src/components/MkSelect.vue
@@ -174,7 +174,7 @@ watch([modelValue, () => props.items], () => {
}, { immediate: true, deep: true });
function show() {
- if (opening.value) return;
+ if (opening.value || props.disabled || props.readonly) return;
focus();
opening.value = true;
diff --git a/packages/frontend/src/components/MkStreamingNotesTimeline.vue b/packages/frontend/src/components/MkStreamingNotesTimeline.vue
index 44f873b6e3..693f551ffc 100644
--- a/packages/frontend/src/components/MkStreamingNotesTimeline.vue
+++ b/packages/frontend/src/components/MkStreamingNotesTimeline.vue
@@ -63,6 +63,7 @@ import { useDocumentVisibility } from '@@/js/use-document-visibility.js';
import { getScrollContainer, scrollToTop } from '@@/js/scroll.js';
import type { BasicTimelineType } from '@/timelines.js';
import type { SoundStore } from '@/preferences/def.js';
+import type { IPaginator, MisskeyEntity } from '@/utility/paginator.js';
import MkPullToRefresh from '@/components/MkPullToRefresh.vue';
import { useStream } from '@/stream.js';
import * as sound from '@/utility/sound.js';
@@ -76,7 +77,6 @@ import { i18n } from '@/i18n.js';
import { globalEvents, useGlobalEvent } from '@/events.js';
import { isSeparatorNeeded, getSeparatorInfo } from '@/utility/timeline-date-separate.js';
import { Paginator } from '@/utility/paginator.js';
-import type { IPaginator, MisskeyEntity } from '@/utility/paginator.js';
const props = withDefaults(defineProps<{
src: BasicTimelineType | 'mentions' | 'directs' | 'list' | 'antenna' | 'channel' | 'role';
@@ -524,7 +524,6 @@ defineExpose({
align-items: center;
justify-content: center;
gap: 1em;
- opacity: 0.75;
padding: 8px 8px;
margin: 0 auto;
border-bottom: solid 0.5px var(--MI_THEME-divider);
diff --git a/packages/frontend/src/components/MkStreamingNotificationsTimeline.vue b/packages/frontend/src/components/MkStreamingNotificationsTimeline.vue
index e21adab36c..0276b7eaee 100644
--- a/packages/frontend/src/components/MkStreamingNotificationsTimeline.vue
+++ b/packages/frontend/src/components/MkStreamingNotificationsTimeline.vue
@@ -46,8 +46,8 @@ import { onUnmounted, onMounted, computed, useTemplateRef, TransitionGroup, mark
import * as Misskey from 'misskey-js';
import { useInterval } from '@@/js/use-interval.js';
import { useDocumentVisibility } from '@@/js/use-document-visibility.js';
-import type { notificationTypes } from '@@/js/const.js';
import { getScrollContainer, scrollToTop } from '@@/js/scroll.js';
+import type { notificationTypes } from '@@/js/const.js';
import XNotification from '@/components/MkNotification.vue';
import MkNote from '@/components/MkNote.vue';
import { useStream } from '@/stream.js';
@@ -235,7 +235,6 @@ defineExpose({
align-items: center;
justify-content: center;
gap: 1em;
- opacity: 0.75;
padding: 8px 8px;
margin: 0 auto;
border-bottom: solid 0.5px var(--MI_THEME-divider);
diff --git a/packages/frontend/src/components/MkTabs.vue b/packages/frontend/src/components/MkTabs.vue
index a1f30100d0..5c4a67b026 100644
--- a/packages/frontend/src/components/MkTabs.vue
+++ b/packages/frontend/src/components/MkTabs.vue
@@ -169,10 +169,6 @@ onUnmounted(() => {
overflow-x: auto;
overflow-y: hidden;
scrollbar-width: none;
-
- &::-webkit-scrollbar {
- display: none;
- }
}
.tabsInner {
diff --git a/packages/frontend/src/components/MkUserList.vue b/packages/frontend/src/components/MkUserList.vue
index c639e18b1d..e3469d0fd7 100644
--- a/packages/frontend/src/components/MkUserList.vue
+++ b/packages/frontend/src/components/MkUserList.vue
@@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-
+
diff --git a/packages/frontend/src/components/global/MkPageHeader.tabs.vue b/packages/frontend/src/components/global/MkPageHeader.tabs.vue
index 255fca8f86..f2173b2e22 100644
--- a/packages/frontend/src/components/global/MkPageHeader.tabs.vue
+++ b/packages/frontend/src/components/global/MkPageHeader.tabs.vue
@@ -194,10 +194,6 @@ onUnmounted(() => {
overflow-x: auto;
overflow-y: hidden;
scrollbar-width: none;
-
- &::-webkit-scrollbar {
- display: none;
- }
}
.tabsInner {
diff --git a/packages/frontend/src/components/global/MkUrl.vue b/packages/frontend/src/components/global/MkUrl.vue
index 1da16b8923..914c495d7a 100644
--- a/packages/frontend/src/components/global/MkUrl.vue
+++ b/packages/frontend/src/components/global/MkUrl.vue
@@ -28,11 +28,11 @@ SPDX-License-Identifier: AGPL-3.0-only
import { defineAsyncComponent, ref } from 'vue';
import { toUnicode as decodePunycode } from 'punycode.js';
import { url as local } from '@@/js/config.js';
+import { maybeMakeRelative } from '@@/js/url.js';
+import type { MkABehavior } from '@/components/global/MkA.vue';
import * as os from '@/os.js';
import { useTooltip } from '@/composables/use-tooltip.js';
import { isEnabledUrlPreview } from '@/utility/url-preview.js';
-import type { MkABehavior } from '@/components/global/MkA.vue';
-import { maybeMakeRelative } from '@@/js/url.js';
function safeURIDecode(str: string): string {
try {
@@ -94,7 +94,7 @@ const target = self ? null : '_blank';
}
.schema {
- opacity: 0.5;
+ color: color(from currentcolor srgb r g b / 0.5); // DOMノード全体をopacityで半透明化するより文字色を半透明化した方が若干レンダリングパフォーマンスが良い
}
.hostname {
@@ -102,11 +102,11 @@ const target = self ? null : '_blank';
}
.pathname {
- opacity: 0.8;
+ color: color(from currentcolor srgb r g b / 0.8); // DOMノード全体をopacityで半透明化するより文字色を半透明化した方が若干レンダリングパフォーマンスが良い
}
.query {
- opacity: 0.5;
+ color: color(from currentcolor srgb r g b / 0.5); // DOMノード全体をopacityで半透明化するより文字色を半透明化した方が若干レンダリングパフォーマンスが良い
}
.hash {
diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts
index 83ad0ebdf9..bf0e5e1b37 100644
--- a/packages/frontend/src/os.ts
+++ b/packages/frontend/src/os.ts
@@ -13,7 +13,7 @@ import type { ComponentProps as CP } from 'vue-component-type-helpers';
import type { Form, GetFormResultType } from '@/utility/form.js';
import type { MenuItem } from '@/types/menu.js';
import type { PostFormProps } from '@/types/post-form.js';
-import type { UploaderDialogFeatures } from '@/components/MkUploaderDialog.vue';
+import type { UploaderFeatures } from '@/composables/use-uploader.js';
import type MkRoleSelectDialog_TypeReferenceOnly from '@/components/MkRoleSelectDialog.vue';
import type MkEmojiPickerDialog_TypeReferenceOnly from '@/components/MkEmojiPickerDialog.vue';
import { misskeyApi } from '@/utility/misskey-api.js';
@@ -837,7 +837,7 @@ export function launchUploader(
options?: {
folderId?: string | null;
multiple?: boolean;
- features?: UploaderDialogFeatures;
+ features?: UploaderFeatures;
},
): Promise
{
return new Promise(async (res, rej) => {
diff --git a/packages/frontend/src/pages/about-misskey.vue b/packages/frontend/src/pages/about-misskey.vue
index 72a3313c95..057deec4cf 100644
--- a/packages/frontend/src/pages/about-misskey.vue
+++ b/packages/frontend/src/pages/about-misskey.vue
@@ -48,7 +48,7 @@ SPDX-License-Identifier: AGPL-3.0-only
- {{ i18n.tsx._aboutMisskey.thisIsModifiedVersion({ name: instance.name }) }}
+ {{ i18n.tsx._aboutMisskey.thisIsModifiedVersion({ name: instance.name ?? host }) }}
@@ -134,7 +134,7 @@ SPDX-License-Identifier: AGPL-3.0-only
diff --git a/packages/frontend/src/pages/admin-file.vue b/packages/frontend/src/pages/admin-file.vue
index 8495642a8c..7a49ba542f 100644
--- a/packages/frontend/src/pages/admin-file.vue
+++ b/packages/frontend/src/pages/admin-file.vue
@@ -44,8 +44,19 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts.delete }}
-
+
+
+
+
{{ i18n.ts.requireAdminForView }}
@@ -86,12 +97,15 @@ import { misskeyApi } from '@/utility/misskey-api.js';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import { iAmAdmin, iAmModerator } from '@/i.js';
+import MkTabs from '@/components/MkTabs.vue';
const tab = ref('overview');
const file = ref(null);
const info = ref(null);
const isSensitive = ref(false);
+const usageTab = ref<'note' | 'chat'>('note');
const XNotes = defineAsyncComponent(() => import('./drive.file.notes.vue'));
+const XChat = defineAsyncComponent(() => import('./admin-file.chat.vue'));
const props = defineProps<{
fileId: string,
@@ -147,9 +161,9 @@ const headerTabs = computed(() => [{
title: i18n.ts.overview,
icon: 'ti ti-info-circle',
}, iAmModerator ? {
- key: 'notes',
- title: i18n.ts._fileViewer.attachedNotes,
- icon: 'ti ti-pencil',
+ key: 'usage',
+ title: i18n.ts._fileViewer.usage,
+ icon: 'ti ti-plus',
} : null, iAmModerator ? {
key: 'ip',
title: 'IP',
diff --git a/packages/frontend/src/pages/chat/message.vue b/packages/frontend/src/pages/chat/message.vue
index a04ec7fd87..834aa9e033 100644
--- a/packages/frontend/src/pages/chat/message.vue
+++ b/packages/frontend/src/pages/chat/message.vue
@@ -25,7 +25,7 @@ import { misskeyApi } from '@/utility/misskey-api.js';
import { definePage } from '@/page.js';
const props = defineProps<{
- messageId?: string;
+ messageId: string;
}>();
const initializing = ref(true);
diff --git a/packages/frontend/src/pages/chat/room.vue b/packages/frontend/src/pages/chat/room.vue
index ac13c5fac6..6443616fe3 100644
--- a/packages/frontend/src/pages/chat/room.vue
+++ b/packages/frontend/src/pages/chat/room.vue
@@ -197,7 +197,7 @@ async function initialize() {
connection.value.on('deleted', onDeleted);
connection.value.on('react', onReact);
connection.value.on('unreact', onUnreact);
- } else {
+ } else if (props.roomId) {
const [rResult, mResult] = await Promise.allSettled([
misskeyApi('chat/rooms/show', { roomId: props.roomId }),
misskeyApi('chat/messages/room-timeline', { roomId: props.roomId, limit: LIMIT }),
diff --git a/packages/frontend/src/pages/clip.vue b/packages/frontend/src/pages/clip.vue
index 8843812544..8176fb519b 100644
--- a/packages/frontend/src/pages/clip.vue
+++ b/packages/frontend/src/pages/clip.vue
@@ -76,7 +76,8 @@ watch(() => props.clipId, async () => {
clip.value = await misskeyApi('clips/show', {
clipId: props.clipId,
});
- favorited.value = clip.value.isFavorited;
+
+ favorited.value = clip.value!.isFavorited ?? false;
}, {
immediate: true,
});
@@ -108,6 +109,8 @@ const headerActions = computed(() => clip.value && isOwned.value ? [{
icon: 'ti ti-pencil',
text: i18n.ts.edit,
handler: async (): Promise => {
+ if (clip.value == null) return;
+
const { canceled, result } = await os.form(clip.value.name, {
name: {
type: 'string',
@@ -128,6 +131,7 @@ const headerActions = computed(() => clip.value && isOwned.value ? [{
default: clip.value.isPublic,
},
});
+
if (canceled) return;
os.apiWithDialog('clips/update', {
@@ -178,6 +182,8 @@ const headerActions = computed(() => clip.value && isOwned.value ? [{
text: i18n.ts.delete,
danger: true,
handler: async (): Promise => {
+ if (clip.value == null) return;
+
const { canceled } = await os.confirm({
type: 'warning',
text: i18n.tsx.deleteAreYouSure({ x: clip.value.name }),
diff --git a/packages/frontend/src/pages/favorites.vue b/packages/frontend/src/pages/favorites.vue
index 72dd2b4a16..b37b9c33c5 100644
--- a/packages/frontend/src/pages/favorites.vue
+++ b/packages/frontend/src/pages/favorites.vue
@@ -10,9 +10,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-
-
-
+
@@ -23,7 +21,6 @@ SPDX-License-Identifier: AGPL-3.0-only
import { markRaw } from 'vue';
import MkPagination from '@/components/MkPagination.vue';
import MkNote from '@/components/MkNote.vue';
-import MkDateSeparatedList from '@/components/MkDateSeparatedList.vue';
import { i18n } from '@/i18n.js';
import { definePage } from '@/page.js';
import { Paginator } from '@/utility/paginator.js';
diff --git a/packages/frontend/src/pages/flash/flash-index.vue b/packages/frontend/src/pages/flash/flash-index.vue
index 43632f55ca..74d8d639f3 100644
--- a/packages/frontend/src/pages/flash/flash-index.vue
+++ b/packages/frontend/src/pages/flash/flash-index.vue
@@ -67,7 +67,7 @@ const router = useRouter();
const tab = ref('featured');
const searchQuery = ref('');
-const searchPaginator = shallowRef
(null);
+const searchPaginator = shallowRef | null>(null);
const searchKey = ref(0);
const featuredFlashsPaginator = markRaw(new Paginator('flash/featured', {
diff --git a/packages/frontend/src/pages/install-extensions.vue b/packages/frontend/src/pages/install-extensions.vue
index 4e814ef84f..1b3c6616cc 100644
--- a/packages/frontend/src/pages/install-extensions.vue
+++ b/packages/frontend/src/pages/install-extensions.vue
@@ -13,7 +13,7 @@ SPDX-License-Identifier: AGPL-3.0-only
{{ i18n.ts._externalResourceInstaller._vendorInfo.endpoint }}
-
+
{{ i18n.ts._externalResourceInstaller._vendorInfo.hashVerify }}
@@ -151,7 +151,7 @@ async function fetch() {
case 'theme':
try {
const metaRaw = parseThemeCode(res.data);
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
+
const { id, props, desc: description, ...meta } = metaRaw;
data.value = {
type: 'theme',
diff --git a/packages/frontend/src/pages/instance-info.vue b/packages/frontend/src/pages/instance-info.vue
index 14a64f0bd5..4be5fa447d 100644
--- a/packages/frontend/src/pages/instance-info.vue
+++ b/packages/frontend/src/pages/instance-info.vue
@@ -8,7 +8,7 @@ SPDX-License-Identifier: AGPL-3.0-only
-
![]()
+
{{ instance.name || `(${i18n.ts.unknown})` }}
diff --git a/packages/frontend/src/pages/list.vue b/packages/frontend/src/pages/list.vue
index 4368aff8be..1261428c1c 100644
--- a/packages/frontend/src/pages/list.vue
+++ b/packages/frontend/src/pages/list.vue
@@ -62,24 +62,29 @@ function fetchList(): void {
}
function like() {
+ if (list.value == null) return;
os.apiWithDialog('users/lists/favorite', {
listId: list.value.id,
}).then(() => {
+ if (list.value == null) return;
list.value.isLiked = true;
list.value.likedCount++;
});
}
function unlike() {
+ if (list.value == null) return;
os.apiWithDialog('users/lists/unfavorite', {
listId: list.value.id,
}).then(() => {
+ if (list.value == null) return;
list.value.isLiked = false;
list.value.likedCount--;
});
}
async function create() {
+ if (list.value == null) return;
const { canceled, result: name } = await os.inputText({
title: i18n.ts.enterListName,
});
diff --git a/packages/frontend/src/pages/my-clips/index.vue b/packages/frontend/src/pages/my-clips/index.vue
index 4c664a0951..f48dc5be4d 100644
--- a/packages/frontend/src/pages/my-clips/index.vue
+++ b/packages/frontend/src/pages/my-clips/index.vue
@@ -64,6 +64,7 @@ async function create() {
default: false,
},
});
+
if (canceled) return;
os.apiWithDialog('clips/create', result);
diff --git a/packages/frontend/src/pages/registry.keys.vue b/packages/frontend/src/pages/registry.keys.vue
index 39575fe1f7..8eb2ab9fd0 100644
--- a/packages/frontend/src/pages/registry.keys.vue
+++ b/packages/frontend/src/pages/registry.keys.vue
@@ -79,7 +79,9 @@ async function createKey() {
default: scope.value.join('/'),
},
});
+
if (canceled) return;
+
os.apiWithDialog('i/registry/set', {
scope: result.scope.split('/'),
key: result.key,
diff --git a/packages/frontend/src/pages/registry.vue b/packages/frontend/src/pages/registry.vue
index 5e59082b50..3762dadd12 100644
--- a/packages/frontend/src/pages/registry.vue
+++ b/packages/frontend/src/pages/registry.vue
@@ -56,7 +56,9 @@ async function createKey() {
label: i18n.ts._registry.scope,
},
});
+
if (canceled) return;
+
os.apiWithDialog('i/registry/set', {
scope: result.scope.split('/'),
key: result.key,
diff --git a/packages/frontend/src/pages/user/home.vue b/packages/frontend/src/pages/user/home.vue
index ea77444afd..ed3ae6a2aa 100644
--- a/packages/frontend/src/pages/user/home.vue
+++ b/packages/frontend/src/pages/user/home.vue
@@ -25,7 +25,6 @@ SPDX-License-Identifier: AGPL-3.0-only
-