summaryrefslogtreecommitdiff
path: root/packages/frontend/src/components
diff options
context:
space:
mode:
authorsyuilo <4439005+syuilo@users.noreply.github.com>2025-08-26 13:34:41 +0900
committersyuilo <4439005+syuilo@users.noreply.github.com>2025-08-26 13:34:41 +0900
commitd6a1046361d3d38726f2a86588960c3614f72a9f (patch)
treed0a501710c36b8e9488e6756adc906c08447910b /packages/frontend/src/components
parentrefactor and fix (diff)
downloadmisskey-d6a1046361d3d38726f2a86588960c3614f72a9f.tar.gz
misskey-d6a1046361d3d38726f2a86588960c3614f72a9f.tar.bz2
misskey-d6a1046361d3d38726f2a86588960c3614f72a9f.zip
refactor
Diffstat (limited to 'packages/frontend/src/components')
-rw-r--r--packages/frontend/src/components/MkAchievements.vue2
-rw-r--r--packages/frontend/src/components/MkChart.vue55
-rw-r--r--packages/frontend/src/components/MkCropperDialog.vue44
-rw-r--r--packages/frontend/src/components/MkEmojiPicker.vue2
-rw-r--r--packages/frontend/src/components/MkEmojiPickerDialog.vue4
-rw-r--r--packages/frontend/src/components/MkModal.vue4
-rw-r--r--packages/frontend/src/components/MkReactionsViewer.reaction.vue27
-rw-r--r--packages/frontend/src/components/global/MkPageHeader.tabs.vue2
8 files changed, 96 insertions, 44 deletions
diff --git a/packages/frontend/src/components/MkAchievements.vue b/packages/frontend/src/components/MkAchievements.vue
index 3b7b59b4d3..bf39c1e983 100644
--- a/packages/frontend/src/components/MkAchievements.vue
+++ b/packages/frontend/src/components/MkAchievements.vue
@@ -16,7 +16,7 @@ SPDX-License-Identifier: AGPL-3.0-only
[$style.iconFrame_platinum]: ACHIEVEMENT_BADGES[achievement.name].frame === 'platinum',
}]"
>
- <div :class="[$style.iconInner]" :style="{ background: ACHIEVEMENT_BADGES[achievement.name].bg }">
+ <div :class="[$style.iconInner]" :style="{ background: ACHIEVEMENT_BADGES[achievement.name].bg ?? '' }">
<img :class="$style.iconImg" :src="ACHIEVEMENT_BADGES[achievement.name].img">
</div>
</div>
diff --git a/packages/frontend/src/components/MkChart.vue b/packages/frontend/src/components/MkChart.vue
index 4d67bba70d..c54081ad42 100644
--- a/packages/frontend/src/components/MkChart.vue
+++ b/packages/frontend/src/components/MkChart.vue
@@ -589,7 +589,10 @@ const fetchDriveFilesChart = async (): Promise<typeof chartData> => {
};
const fetchInstanceRequestsChart = async (): Promise<typeof chartData> => {
- const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span });
+ const host = props.args?.host;
+ if (host == null) return { series: [] };
+
+ const raw = await misskeyApiGet('charts/instance', { host: host, limit: props.limit, span: props.span });
return {
series: [{
name: 'In',
@@ -611,7 +614,10 @@ const fetchInstanceRequestsChart = async (): Promise<typeof chartData> => {
};
const fetchInstanceUsersChart = async (total: boolean): Promise<typeof chartData> => {
- const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span });
+ const host = props.args?.host;
+ if (host == null) return { series: [] };
+
+ const raw = await misskeyApiGet('charts/instance', { host: host, limit: props.limit, span: props.span });
return {
series: [{
name: 'Users',
@@ -626,7 +632,10 @@ const fetchInstanceUsersChart = async (total: boolean): Promise<typeof chartData
};
const fetchInstanceNotesChart = async (total: boolean): Promise<typeof chartData> => {
- const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span });
+ const host = props.args?.host;
+ if (host == null) return { series: [] };
+
+ const raw = await misskeyApiGet('charts/instance', { host: host, limit: props.limit, span: props.span });
return {
series: [{
name: 'Notes',
@@ -641,7 +650,10 @@ const fetchInstanceNotesChart = async (total: boolean): Promise<typeof chartData
};
const fetchInstanceFfChart = async (total: boolean): Promise<typeof chartData> => {
- const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span });
+ const host = props.args?.host;
+ if (host == null) return { series: [] };
+
+ const raw = await misskeyApiGet('charts/instance', { host: host, limit: props.limit, span: props.span });
return {
series: [{
name: 'Following',
@@ -664,7 +676,10 @@ const fetchInstanceFfChart = async (total: boolean): Promise<typeof chartData> =
};
const fetchInstanceDriveUsageChart = async (total: boolean): Promise<typeof chartData> => {
- const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span });
+ const host = props.args?.host;
+ if (host == null) return { series: [] };
+
+ const raw = await misskeyApiGet('charts/instance', { host: host, limit: props.limit, span: props.span });
return {
bytes: true,
series: [{
@@ -680,7 +695,10 @@ const fetchInstanceDriveUsageChart = async (total: boolean): Promise<typeof char
};
const fetchInstanceDriveFilesChart = async (total: boolean): Promise<typeof chartData> => {
- const raw = await misskeyApiGet('charts/instance', { host: props.args?.host, limit: props.limit, span: props.span });
+ const host = props.args?.host;
+ if (host == null) return { series: [] };
+
+ const raw = await misskeyApiGet('charts/instance', { host: host, limit: props.limit, span: props.span });
return {
series: [{
name: 'Drive files',
@@ -695,7 +713,10 @@ const fetchInstanceDriveFilesChart = async (total: boolean): Promise<typeof char
};
const fetchPerUserNotesChart = async (): Promise<typeof chartData> => {
- const raw = await misskeyApiGet('charts/user/notes', { userId: props.args?.user?.id, limit: props.limit, span: props.span });
+ const userId = props.args?.user?.id;
+ if (userId == null) return { series: [] };
+
+ const raw = await misskeyApiGet('charts/user/notes', { userId: userId, limit: props.limit, span: props.span });
return {
series: [...(props.args?.withoutAll ? [] : [{
name: 'All',
@@ -727,7 +748,10 @@ const fetchPerUserNotesChart = async (): Promise<typeof chartData> => {
};
const fetchPerUserPvChart = async (): Promise<typeof chartData> => {
- const raw = await misskeyApiGet('charts/user/pv', { userId: props.args?.user?.id, limit: props.limit, span: props.span });
+ const userId = props.args?.user?.id;
+ if (userId == null) return { series: [] };
+
+ const raw = await misskeyApiGet('charts/user/pv', { userId: userId, limit: props.limit, span: props.span });
return {
series: [{
name: 'Unique PV (user)',
@@ -754,7 +778,10 @@ const fetchPerUserPvChart = async (): Promise<typeof chartData> => {
};
const fetchPerUserFollowingChart = async (): Promise<typeof chartData> => {
- const raw = await misskeyApiGet('charts/user/following', { userId: props.args?.user?.id, limit: props.limit, span: props.span });
+ const userId = props.args?.user?.id;
+ if (userId == null) return { series: [] };
+
+ const raw = await misskeyApiGet('charts/user/following', { userId: userId, limit: props.limit, span: props.span });
return {
series: [{
name: 'Local',
@@ -769,7 +796,10 @@ const fetchPerUserFollowingChart = async (): Promise<typeof chartData> => {
};
const fetchPerUserFollowersChart = async (): Promise<typeof chartData> => {
- const raw = await misskeyApiGet('charts/user/following', { userId: props.args?.user?.id, limit: props.limit, span: props.span });
+ const userId = props.args?.user?.id;
+ if (userId == null) return { series: [] };
+
+ const raw = await misskeyApiGet('charts/user/following', { userId: userId, limit: props.limit, span: props.span });
return {
series: [{
name: 'Local',
@@ -784,7 +814,10 @@ const fetchPerUserFollowersChart = async (): Promise<typeof chartData> => {
};
const fetchPerUserDriveChart = async (): Promise<typeof chartData> => {
- const raw = await misskeyApiGet('charts/user/drive', { userId: props.args?.user?.id, limit: props.limit, span: props.span });
+ const userId = props.args?.user?.id;
+ if (userId == null) return { series: [] };
+
+ const raw = await misskeyApiGet('charts/user/drive', { userId: userId, limit: props.limit, span: props.span });
return {
bytes: true,
series: [{
diff --git a/packages/frontend/src/components/MkCropperDialog.vue b/packages/frontend/src/components/MkCropperDialog.vue
index 7f592fba79..6c07eac47a 100644
--- a/packages/frontend/src/components/MkCropperDialog.vue
+++ b/packages/frontend/src/components/MkCropperDialog.vue
@@ -29,7 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
-import { onMounted, useTemplateRef, ref } from 'vue';
+import { onMounted, useTemplateRef, ref, onUnmounted } from 'vue';
import * as Misskey from 'misskey-js';
import Cropper from 'cropperjs';
import tinycolor from 'tinycolor2';
@@ -55,17 +55,19 @@ const imgEl = useTemplateRef('imgEl');
let cropper: Cropper | null = null;
const loading = ref(true);
-const ok = async () => {
- const promise = new Promise<Misskey.entities.DriveFile>(async (res) => {
- const croppedImage = await cropper?.getCropperImage();
- const croppedSection = await cropper?.getCropperSelection();
+async function ok() {
+ const promise = new Promise<Blob>(async (res) => {
+ if (cropper == null) throw new Error('Cropper is not initialized');
+
+ const croppedImage = await cropper.getCropperImage()!;
+ const croppedSection = await cropper.getCropperSelection()!;
// 拡大率を計算し、(ほぼ)元の大きさに戻す
const zoomedRate = croppedImage.getBoundingClientRect().width / croppedImage.clientWidth;
const widthToRender = croppedSection.getBoundingClientRect().width / zoomedRate;
- const croppedCanvas = await croppedSection?.$toCanvas({ width: widthToRender });
- croppedCanvas?.toBlob(blob => {
+ const croppedCanvas = await croppedSection.$toCanvas({ width: widthToRender });
+ croppedCanvas.toBlob(blob => {
if (!blob) return;
res(blob);
});
@@ -74,25 +76,27 @@ const ok = async () => {
const f = await promise;
emit('ok', f);
- dialogEl.value!.close();
-};
+ if (dialogEl.value != null) dialogEl.value.close();
+}
-const cancel = () => {
+function cancel() {
emit('cancel');
- dialogEl.value!.close();
-};
+ if (dialogEl.value != null) dialogEl.value.close();
+}
-const onImageLoad = () => {
+function onImageLoad() {
loading.value = false;
if (cropper) {
cropper.getCropperImage()!.$center('contain');
cropper.getCropperSelection()!.$center();
}
-};
+}
onMounted(() => {
- cropper = new Cropper(imgEl.value!, {
+ if (imgEl.value == null) return; // TSを黙らすため
+
+ cropper = new Cropper(imgEl.value, {
});
const computedStyle = getComputedStyle(window.document.documentElement);
@@ -104,16 +108,22 @@ onMounted(() => {
selection.outlined = true;
window.setTimeout(() => {
- cropper!.getCropperImage()!.$center('contain');
+ if (cropper == null) return;
+ cropper.getCropperImage()!.$center('contain');
selection.$center();
}, 100);
// モーダルオープンアニメーションが終わったあとで再度調整
window.setTimeout(() => {
- cropper!.getCropperImage()!.$center('contain');
+ if (cropper == null) return;
+ cropper.getCropperImage()!.$center('contain');
selection.$center();
}, 500);
});
+
+onUnmounted(() => {
+ URL.revokeObjectURL(imgUrl);
+});
</script>
<style lang="scss" scoped>
diff --git a/packages/frontend/src/components/MkEmojiPicker.vue b/packages/frontend/src/components/MkEmojiPicker.vue
index df05bcc94c..6904c417ce 100644
--- a/packages/frontend/src/components/MkEmojiPicker.vue
+++ b/packages/frontend/src/components/MkEmojiPicker.vue
@@ -152,7 +152,7 @@ const props = withDefaults(defineProps<{
asDrawer?: boolean;
asWindow?: boolean;
asReactionPicker?: boolean; // 今は使われてないが将来的に使いそう
- targetNote?: Misskey.entities.Note;
+ targetNote?: Misskey.entities.Note | null;
}>(), {
showPinned: true,
});
diff --git a/packages/frontend/src/components/MkEmojiPickerDialog.vue b/packages/frontend/src/components/MkEmojiPickerDialog.vue
index 1627dc8760..0ff4e8f38d 100644
--- a/packages/frontend/src/components/MkEmojiPickerDialog.vue
+++ b/packages/frontend/src/components/MkEmojiPickerDialog.vue
@@ -44,11 +44,11 @@ import { prefer } from '@/preferences.js';
const props = withDefaults(defineProps<{
manualShowing?: boolean | null;
- anchorElement?: HTMLElement;
+ anchorElement?: HTMLElement | null;
showPinned?: boolean;
pinnedEmojis?: string[],
asReactionPicker?: boolean;
- targetNote?: Misskey.entities.Note;
+ targetNote?: Misskey.entities.Note | null;
choseAndClose?: boolean;
}>(), {
manualShowing: null,
diff --git a/packages/frontend/src/components/MkModal.vue b/packages/frontend/src/components/MkModal.vue
index 06686ddfc0..660d5a26be 100644
--- a/packages/frontend/src/components/MkModal.vue
+++ b/packages/frontend/src/components/MkModal.vue
@@ -91,7 +91,7 @@ const emit = defineEmits<{
(ev: 'opened'): void;
(ev: 'click'): void;
(ev: 'esc'): void;
- (ev: 'close'): void;
+ (ev: 'close'): void; // TODO: (refactor) closing に改名する
(ev: 'closed'): void;
}>();
@@ -148,7 +148,6 @@ function close(opts: { useSendAnimation?: boolean } = {}) {
useSendAnime.value = true;
}
- // eslint-disable-next-line vue/no-mutating-props
if (props.anchorElement) props.anchorElement.style.pointerEvents = 'auto';
showing.value = false;
emit('close');
@@ -319,7 +318,6 @@ const alignObserver = new ResizeObserver((entries, observer) => {
onMounted(() => {
watch(() => props.anchorElement, async () => {
if (props.anchorElement) {
- // eslint-disable-next-line vue/no-mutating-props
props.anchorElement.style.pointerEvents = 'none';
}
fixed.value = (type.value === 'drawer') || (getFixedContainer(props.anchorElement) != null);
diff --git a/packages/frontend/src/components/MkReactionsViewer.reaction.vue b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
index e0490404ac..d96f0e2420 100644
--- a/packages/frontend/src/components/MkReactionsViewer.reaction.vue
+++ b/packages/frontend/src/components/MkReactionsViewer.reaction.vue
@@ -58,18 +58,22 @@ const emit = defineEmits<{
const buttonEl = useTemplateRef('buttonEl');
const emojiName = computed(() => props.reaction.replace(/:/g, '').replace(/@\./, ''));
-const emoji = computed(() => customEmojisMap.get(emojiName.value) ?? getUnicodeEmoji(props.reaction));
const canToggle = computed(() => {
+ const emoji = customEmojisMap.get(emojiName.value) ?? getUnicodeEmoji(props.reaction);
+
// TODO
- //return !props.reaction.match(/@\w/) && $i && emoji.value && checkReactionPermissions($i, props.note, emoji.value);
- return !props.reaction.match(/@\w/) && $i && emoji.value;
+ //return !props.reaction.match(/@\w/) && $i && emoji && checkReactionPermissions($i, props.note, emoji);
+ return !props.reaction.match(/@\w/) && $i && emoji;
});
const canGetInfo = computed(() => !props.reaction.match(/@\w/) && props.reaction.includes(':'));
const isLocalCustomEmoji = props.reaction[0] === ':' && props.reaction.includes('@.');
async function toggleReaction() {
if (!canToggle.value) return;
+ if ($i == null) return;
+
+ const me = $i;
const oldReaction = props.myReaction;
if (oldReaction) {
@@ -93,7 +97,7 @@ async function toggleReaction() {
noteId: props.noteId,
}).then(() => {
noteEvents.emit(`unreacted:${props.noteId}`, {
- userId: $i!.id,
+ userId: me.id,
reaction: oldReaction,
});
if (oldReaction !== props.reaction) {
@@ -101,10 +105,12 @@ async function toggleReaction() {
noteId: props.noteId,
reaction: props.reaction,
}).then(() => {
+ const emoji = customEmojisMap.get(emojiName.value);
+ if (emoji == null) return;
noteEvents.emit(`reacted:${props.noteId}`, {
- userId: $i!.id,
+ userId: me.id,
reaction: props.reaction,
- emoji: emoji.value,
+ emoji: emoji,
});
});
}
@@ -131,10 +137,13 @@ async function toggleReaction() {
noteId: props.noteId,
reaction: props.reaction,
}).then(() => {
+ const emoji = customEmojisMap.get(emojiName.value);
+ if (emoji == null) return;
+
noteEvents.emit(`reacted:${props.noteId}`, {
- userId: $i!.id,
+ userId: me.id,
reaction: props.reaction,
- emoji: emoji.value,
+ emoji: emoji,
});
});
// TODO: 上位コンポーネントでやる
@@ -217,6 +226,8 @@ onMounted(() => {
if (!mock) {
useTooltip(buttonEl, async (showing) => {
+ if (buttonEl.value == null) return;
+
const reactions = await misskeyApiGet('notes/reactions', {
noteId: props.noteId,
type: props.reaction,
diff --git a/packages/frontend/src/components/global/MkPageHeader.tabs.vue b/packages/frontend/src/components/global/MkPageHeader.tabs.vue
index f2173b2e22..ae051eaf2c 100644
--- a/packages/frontend/src/components/global/MkPageHeader.tabs.vue
+++ b/packages/frontend/src/components/global/MkPageHeader.tabs.vue
@@ -59,7 +59,7 @@ import { prefer } from '@/preferences.js';
const props = withDefaults(defineProps<{
tabs?: Tab[];
tab?: string;
- rootEl?: HTMLElement;
+ rootEl?: HTMLElement | null;
}>(), {
tabs: () => ([] as Tab[]),
});