diff options
| author | syuilo <4439005+syuilo@users.noreply.github.com> | 2025-08-26 13:34:41 +0900 |
|---|---|---|
| committer | syuilo <4439005+syuilo@users.noreply.github.com> | 2025-08-26 13:34:41 +0900 |
| commit | d6a1046361d3d38726f2a86588960c3614f72a9f (patch) | |
| tree | d0a501710c36b8e9488e6756adc906c08447910b /packages/frontend/src/components | |
| parent | refactor and fix (diff) | |
| download | misskey-d6a1046361d3d38726f2a86588960c3614f72a9f.tar.gz misskey-d6a1046361d3d38726f2a86588960c3614f72a9f.tar.bz2 misskey-d6a1046361d3d38726f2a86588960c3614f72a9f.zip | |
refactor
Diffstat (limited to 'packages/frontend/src/components')
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[]), }); |